diff --git a/lab_3/aimenv/Scripts/Activate.ps1 b/lab_3/aimenv/Scripts/Activate.ps1 new file mode 100644 index 0000000..3045241 --- /dev/null +++ b/lab_3/aimenv/Scripts/Activate.ps1 @@ -0,0 +1,502 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" + +# SIG # Begin signature block +# MIIvIwYJKoZIhvcNAQcCoIIvFDCCLxACAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk +# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h +# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z +# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z +# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ +# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s +# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL +# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb +# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 +# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c +# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx +# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 +# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL +# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud +# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf +# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk +# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS +# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK +# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB +# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp +# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg +# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri +# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 +# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 +# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 +# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H +# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G +# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C +# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce +# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da +# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T +# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA +# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh +# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM +# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z +# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 +# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY +# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP +# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T +# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD +# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG +# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY +# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj +# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV +# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN +# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry +# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL +# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf +# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh +# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh +# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV +# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j +# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH +# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC +# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l +# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW +# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw +# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x +# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ +# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7 +# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx +# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb +# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA +# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm +# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA +# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1 +# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc +# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w +# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B +# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj +# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E +# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM +# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI +# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v +# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex +# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v +# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF +# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6 +# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu +# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI +# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N +# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA +# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2 +# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O +# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd +# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50 +# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU +# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq +# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs +# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa +# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa +# tjCCGrICAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT +# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl +# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC +# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62 +# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA +# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAyAC4ANQBfADIAMAAyADQA +# MAA4ADAANgAuADAAMaECgAAwDQYJKoZIhvcNAQEBBQAEggIAoXbLeBCFQhwr4rTK +# R0WSySG7AtpuY1n5vhwkJPE0JgQ11PFJYphroU2ouWWM8ifejqa6m21JEWGjC9En +# Rpzpe1+eps7ClsdO+y5NxZc/3vD1j7IddJdzZh77QqDFMqJEeDNY+00OxxnnhbN1 +# wJk29w8qRyIJ7HpCM0E5b8R8Atooip5ihAgrdrIsyyA3Mnl5Y+YMdqtQYe4QtOhE +# QcEoxAMoI5nLSGsbLhEM8CArl36EmX31eHTVMRJMaM98p0DkURHL030ALmW2V70h +# M7ovmhOezFyndR1d3HtcfwRB3nr5vHWZe6ythZ3wVgpsN++RdDOvHjb9LC9lkth/ +# BGbcmVqsA9ZHnub1iPt89GsQBSiXjaOnWUxgJi0Qd3s2pwswLxHp05QDUE/d8EF7 +# Wy6aNPI43+G2BjPLVeM3iVbMWd/yxhH6pddaVPAMKVvxJoJ7PfDLihMNyonHt0on +# xuaM5r2KaVMWpHIkgLiB9tyvdIQb0IW+YU05VAnOqh7CDaEtP7jM6P0usxY9ufEC +# BFZnOGb3M/c4KbcOuHOIkY3jGqw+DLZFrcWiIe2wbi2TsXDixs+pz8vm/KQczrQ2 +# RJ1R8jrbK7IIRyZmTYf+dStZG3NhNQn1xcPYraHKNOm9CzNmeXJTdfAe0BEApqUN +# 9AiLj6uvSEp278ysr/EE3ayw2Qmhghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcn +# BgkqhkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3 +# DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgpuSq +# fyINa45wSs5Sa6msoQk+zCLDcSK24OqaBM/0/2cCEFtb0VJATq3jxU9l7ewmqjcY +# DzIwMjQwODA2MjEwMDM5WqCCEwkwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f +# 5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp +# Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 +# IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEz +# MjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x +# IDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0B +# AQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6 +# OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp +# 52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF +# 6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G4 +# 5lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7p +# Be6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAs +# NJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU +# 6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwK +# WEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFx +# smxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbR +# yV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPL +# QHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG +# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG +# SAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4E +# FgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov +# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1 +# NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUH +# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDov +# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNI +# QTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCp +# tZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX +# 4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoF +# eoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+nji +# kxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBB +# Jt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJ +# A+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcs +# QdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE +# 5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS +# 2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3 +# CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUb +# c7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqADAgECAhAHNje3JFR8 +# 2Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0z +# NzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg +# SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1 +# NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +# AQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI +# 82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9 +# xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ +# 3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5Emfv +# DqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDET +# qVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHe +# IhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jo +# n7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ +# 9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/T +# Xkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJg +# o1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkw +# EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+e +# yG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQD +# AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF +# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw +# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy +# dDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln +# aUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg +# hkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGw +# GC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0 +# MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1D +# X+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw +# 1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY +# +/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0I +# SQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr +# 5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7y +# Rp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDop +# hrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/ +# AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMO +# Hds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkq +# hkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j +# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB +# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5 +# WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1K +# PDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2r +# snnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C +# 8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBf +# sXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +# QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8 +# rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaY +# dj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+ +# wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw +# ++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+N +# P8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7F +# wI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUw +# AwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAU +# Reuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEB +# BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG +# AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 +# cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAow +# CDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/ +# Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLe +# JLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE +# 1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9Hda +# XFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbO +# byMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYID +# djCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYg +# VGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUA +# oIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcN +# MjQwODA2MjEwMDM5WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4Jd +# zqqKycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQglCIBxGudJQwqEBh+XAoT3nqSoAuS +# uMjmJTX95zFjdk0wNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBX +# aEE2b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIAOkILAZviyFOU +# Qzt10RYNFHl0zO4rgXcR5oCeJlU1n9y+DwjCTvcrax9qdkEuiEJWDewXbak3TPQK +# 0ts7jhUIFMDTEn8GZXysruzDlYNLstKM4RbYIK+f2772phehvABS5mn70+L63GXe +# A5UFYM5M7BAvEY+3DKEwUnN9lAl8YKi1xS545MXYm1B96gI/7oEBDkNV2DoNIZAw +# R2B4wPTcpI2aG5zZ0jFgVtq8bOXLZ9b9pBrhKbf4PZWxPqAFwUtZryQKdt770u3Y +# l0WR2SgemKq4aOEvajD1J4fC56lnUoekXt4yH8/fBueCXYx+ADoEkU4/ota7C1oL +# aCZE4G0iQOH9XFtMUjA87oEPisJG63onir6tsurTjjm/wK8VnFQBSii4ILtfSOfR +# kDMsu7kS0H5SWliY3sPlDTn4Kwl14EThMmyXUr7SFFHnsibHtfLATTmV6XyeJ03l +# BmwDl8hdzt5G0pjH/u3bTFcdJu7J0RQuGYgpmNsVYjHCQnZDrJjzIE2os/QYgL6D +# B/ZYSv96jnYs6cFd93R0ixZMsQPQKcs2gbVYz3nymJL7t605LzW86tENmORsUdgm +# qh0ky+qe/+D/f88WLLjdHi/xfskiFKEL66Y4EWkECoUUMBRcJlIg1GszTCVmwD1N +# foIJo8CaFGMoR+QHwDeamNbOOlrCFMQ= +# SIG # End signature block diff --git a/lab_3/aimenv/Scripts/activate b/lab_3/aimenv/Scripts/activate new file mode 100644 index 0000000..8cf30bf --- /dev/null +++ b/lab_3/aimenv/Scripts/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath "C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv") +else + # use the path as-is + export VIRTUAL_ENV="C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv" +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/Scripts:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(aimenv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(aimenv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/lab_3/aimenv/Scripts/activate.bat b/lab_3/aimenv/Scripts/activate.bat new file mode 100644 index 0000000..11bc012 --- /dev/null +++ b/lab_3/aimenv/Scripts/activate.bat @@ -0,0 +1,34 @@ +@echo off + +rem This file is UTF-8 encoded, so we need to update the current code page while executing it +for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( + set _OLD_CODEPAGE=%%a +) +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul +) + +set VIRTUAL_ENV=C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv + +if not defined PROMPT set PROMPT=$P$G + +if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% +if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% + +set _OLD_VIRTUAL_PROMPT=%PROMPT% +set PROMPT=(aimenv) %PROMPT% + +if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% +set PYTHONHOME= + +if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% +if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% + +set PATH=%VIRTUAL_ENV%\Scripts;%PATH% +set VIRTUAL_ENV_PROMPT=(aimenv) + +:END +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + set _OLD_CODEPAGE= +) diff --git a/lab_3/aimenv/Scripts/deactivate.bat b/lab_3/aimenv/Scripts/deactivate.bat new file mode 100644 index 0000000..62a39a7 --- /dev/null +++ b/lab_3/aimenv/Scripts/deactivate.bat @@ -0,0 +1,22 @@ +@echo off + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) +set _OLD_VIRTUAL_PROMPT= + +if defined _OLD_VIRTUAL_PYTHONHOME ( + set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + set _OLD_VIRTUAL_PYTHONHOME= +) + +if defined _OLD_VIRTUAL_PATH ( + set "PATH=%_OLD_VIRTUAL_PATH%" +) + +set _OLD_VIRTUAL_PATH= + +set VIRTUAL_ENV= +set VIRTUAL_ENV_PROMPT= + +:END diff --git a/lab_3/aimenv/Scripts/debugpy.exe b/lab_3/aimenv/Scripts/debugpy.exe new file mode 100644 index 0000000..6d0e480 Binary files /dev/null and b/lab_3/aimenv/Scripts/debugpy.exe differ diff --git a/lab_3/aimenv/Scripts/ipython.exe b/lab_3/aimenv/Scripts/ipython.exe new file mode 100644 index 0000000..c5d0b33 Binary files /dev/null and b/lab_3/aimenv/Scripts/ipython.exe differ diff --git a/lab_3/aimenv/Scripts/ipython3.exe b/lab_3/aimenv/Scripts/ipython3.exe new file mode 100644 index 0000000..c5d0b33 Binary files /dev/null and b/lab_3/aimenv/Scripts/ipython3.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter-kernel.exe b/lab_3/aimenv/Scripts/jupyter-kernel.exe new file mode 100644 index 0000000..cc1b7f4 Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter-kernel.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter-kernelspec.exe b/lab_3/aimenv/Scripts/jupyter-kernelspec.exe new file mode 100644 index 0000000..72bf4e7 Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter-kernelspec.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter-migrate.exe b/lab_3/aimenv/Scripts/jupyter-migrate.exe new file mode 100644 index 0000000..2033e15 Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter-migrate.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter-run.exe b/lab_3/aimenv/Scripts/jupyter-run.exe new file mode 100644 index 0000000..1d34b22 Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter-run.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter-troubleshoot.exe b/lab_3/aimenv/Scripts/jupyter-troubleshoot.exe new file mode 100644 index 0000000..de9c4d6 Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter-troubleshoot.exe differ diff --git a/lab_3/aimenv/Scripts/jupyter.exe b/lab_3/aimenv/Scripts/jupyter.exe new file mode 100644 index 0000000..77e316a Binary files /dev/null and b/lab_3/aimenv/Scripts/jupyter.exe differ diff --git a/lab_3/aimenv/Scripts/pip.exe b/lab_3/aimenv/Scripts/pip.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_3/aimenv/Scripts/pip.exe differ diff --git a/lab_3/aimenv/Scripts/pip3.12.exe b/lab_3/aimenv/Scripts/pip3.12.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_3/aimenv/Scripts/pip3.12.exe differ diff --git a/lab_3/aimenv/Scripts/pip3.exe b/lab_3/aimenv/Scripts/pip3.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_3/aimenv/Scripts/pip3.exe differ diff --git a/lab_3/aimenv/Scripts/pygmentize.exe b/lab_3/aimenv/Scripts/pygmentize.exe new file mode 100644 index 0000000..9c1459b Binary files /dev/null and b/lab_3/aimenv/Scripts/pygmentize.exe differ diff --git a/lab_3/aimenv/Scripts/python.exe b/lab_3/aimenv/Scripts/python.exe new file mode 100644 index 0000000..b58faef Binary files /dev/null and b/lab_3/aimenv/Scripts/python.exe differ diff --git a/lab_3/aimenv/Scripts/pythonw.exe b/lab_3/aimenv/Scripts/pythonw.exe new file mode 100644 index 0000000..ca33b90 Binary files /dev/null and b/lab_3/aimenv/Scripts/pythonw.exe differ diff --git a/lab_3/aimenv/Scripts/pywin32_postinstall.py b/lab_3/aimenv/Scripts/pywin32_postinstall.py new file mode 100644 index 0000000..147f0cd --- /dev/null +++ b/lab_3/aimenv/Scripts/pywin32_postinstall.py @@ -0,0 +1,783 @@ +# postinstall script for pywin32 +# +# copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory, +# and creates a pth file +import glob +import os +import shutil +import sys +import sysconfig + +try: + import winreg as winreg +except: + import winreg + +# Send output somewhere so it can be found if necessary... +import tempfile + +tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w") + + +class Tee: + def __init__(self, file): + self.f = file + + def write(self, what): + if self.f is not None: + try: + self.f.write(what.replace("\n", "\r\n")) + except IOError: + pass + tee_f.write(what) + + def flush(self): + if self.f is not None: + try: + self.f.flush() + except IOError: + pass + tee_f.flush() + + +# For some unknown reason, when running under bdist_wininst we will start up +# with sys.stdout as None but stderr is hooked up. This work-around allows +# bdist_wininst to see the output we write and display it at the end of +# the install. +if sys.stdout is None: + sys.stdout = sys.stderr + +sys.stderr = Tee(sys.stderr) +sys.stdout = Tee(sys.stdout) + +com_modules = [ + # module_name, class_names + ("win32com.servers.interp", "Interpreter"), + ("win32com.servers.dictionary", "DictionaryPolicy"), + ("win32com.axscript.client.pyscript", "PyScript"), +] + +# Is this a 'silent' install - ie, avoid all dialogs. +# Different than 'verbose' +silent = 0 + +# Verbosity of output messages. +verbose = 1 + +root_key_name = "Software\\Python\\PythonCore\\" + sys.winver + +try: + # When this script is run from inside the bdist_wininst installer, + # file_created() and directory_created() are additional builtin + # functions which write lines to Python23\pywin32-install.log. This is + # a list of actions for the uninstaller, the format is inspired by what + # the Wise installer also creates. + file_created + is_bdist_wininst = True +except NameError: + is_bdist_wininst = False # we know what it is not - but not what it is :) + + def file_created(file): + pass + + def directory_created(directory): + pass + + def get_root_hkey(): + try: + winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY + ) + return winreg.HKEY_LOCAL_MACHINE + except OSError: + # Either not exist, or no permissions to create subkey means + # must be HKCU + return winreg.HKEY_CURRENT_USER + + +try: + create_shortcut +except NameError: + # Create a function with the same signature as create_shortcut provided + # by bdist_wininst + def create_shortcut( + path, description, filename, arguments="", workdir="", iconpath="", iconindex=0 + ): + import pythoncom + from win32com.shell import shell + + ilink = pythoncom.CoCreateInstance( + shell.CLSID_ShellLink, + None, + pythoncom.CLSCTX_INPROC_SERVER, + shell.IID_IShellLink, + ) + ilink.SetPath(path) + ilink.SetDescription(description) + if arguments: + ilink.SetArguments(arguments) + if workdir: + ilink.SetWorkingDirectory(workdir) + if iconpath or iconindex: + ilink.SetIconLocation(iconpath, iconindex) + # now save it. + ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile) + ipf.Save(filename, 0) + + # Support the same list of "path names" as bdist_wininst. + def get_special_folder_path(path_name): + from win32com.shell import shell, shellcon + + for maybe in """ + CSIDL_COMMON_STARTMENU CSIDL_STARTMENU CSIDL_COMMON_APPDATA + CSIDL_LOCAL_APPDATA CSIDL_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY + CSIDL_DESKTOPDIRECTORY CSIDL_COMMON_STARTUP CSIDL_STARTUP + CSIDL_COMMON_PROGRAMS CSIDL_PROGRAMS CSIDL_PROGRAM_FILES_COMMON + CSIDL_PROGRAM_FILES CSIDL_FONTS""".split(): + if maybe == path_name: + csidl = getattr(shellcon, maybe) + return shell.SHGetSpecialFolderPath(0, csidl, False) + raise ValueError("%s is an unknown path ID" % (path_name,)) + + +def CopyTo(desc, src, dest): + import win32api + import win32con + + while 1: + try: + win32api.CopyFile(src, dest, 0) + return + except win32api.error as details: + if details.winerror == 5: # access denied - user not admin. + raise + if silent: + # Running silent mode - just re-raise the error. + raise + full_desc = ( + "Error %s\n\n" + "If you have any Python applications running, " + "please close them now\nand select 'Retry'\n\n%s" + % (desc, details.strerror) + ) + rc = win32api.MessageBox( + 0, full_desc, "Installation Error", win32con.MB_ABORTRETRYIGNORE + ) + if rc == win32con.IDABORT: + raise + elif rc == win32con.IDIGNORE: + return + # else retry - around we go again. + + +# We need to import win32api to determine the Windows system directory, +# so we can copy our system files there - but importing win32api will +# load the pywintypes.dll already in the system directory preventing us +# from updating them! +# So, we pull the same trick pywintypes.py does, but it loads from +# our pywintypes_system32 directory. +def LoadSystemModule(lib_dir, modname): + # See if this is a debug build. + import importlib.machinery + import importlib.util + + suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" + filename = "%s%d%d%s.dll" % ( + modname, + sys.version_info[0], + sys.version_info[1], + suffix, + ) + filename = os.path.join(lib_dir, "pywin32_system32", filename) + loader = importlib.machinery.ExtensionFileLoader(modname, filename) + spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + +def SetPyKeyVal(key_name, value_name, value): + root_hkey = get_root_hkey() + root_key = winreg.OpenKey(root_hkey, root_key_name) + try: + my_key = winreg.CreateKey(root_key, key_name) + try: + winreg.SetValueEx(my_key, value_name, 0, winreg.REG_SZ, value) + if verbose: + print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value)) + finally: + my_key.Close() + finally: + root_key.Close() + + +def UnsetPyKeyVal(key_name, value_name, delete_key=False): + root_hkey = get_root_hkey() + root_key = winreg.OpenKey(root_hkey, root_key_name) + try: + my_key = winreg.OpenKey(root_key, key_name, 0, winreg.KEY_SET_VALUE) + try: + winreg.DeleteValue(my_key, value_name) + if verbose: + print("-> DELETE %s\\%s[%s]" % (root_key_name, key_name, value_name)) + finally: + my_key.Close() + if delete_key: + winreg.DeleteKey(root_key, key_name) + if verbose: + print("-> DELETE %s\\%s" % (root_key_name, key_name)) + except OSError as why: + winerror = getattr(why, "winerror", why.errno) + if winerror != 2: # file not found + raise + finally: + root_key.Close() + + +def RegisterCOMObjects(register=True): + import win32com.server.register + + if register: + func = win32com.server.register.RegisterClasses + else: + func = win32com.server.register.UnregisterClasses + flags = {} + if not verbose: + flags["quiet"] = 1 + for module, klass_name in com_modules: + __import__(module) + mod = sys.modules[module] + flags["finalize_register"] = getattr(mod, "DllRegisterServer", None) + flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None) + klass = getattr(mod, klass_name) + func(klass, **flags) + + +def RegisterHelpFile(register=True, lib_dir=None): + if lib_dir is None: + lib_dir = sysconfig.get_paths()["platlib"] + if register: + # Register the .chm help file. + chm_file = os.path.join(lib_dir, "PyWin32.chm") + if os.path.isfile(chm_file): + # This isn't recursive, so if 'Help' doesn't exist, we croak + SetPyKeyVal("Help", None, None) + SetPyKeyVal("Help\\Pythonwin Reference", None, chm_file) + return chm_file + else: + print("NOTE: PyWin32.chm can not be located, so has not " "been registered") + else: + UnsetPyKeyVal("Help\\Pythonwin Reference", None, delete_key=True) + return None + + +def RegisterPythonwin(register=True, lib_dir=None): + """Add (or remove) Pythonwin to context menu for python scripts. + ??? Should probably also add Edit command for pys files also. + Also need to remove these keys on uninstall, but there's no function + like file_created to add registry entries to uninstall log ??? + """ + import os + + if lib_dir is None: + lib_dir = sysconfig.get_paths()["platlib"] + classes_root = get_root_hkey() + ## Installer executable doesn't seem to pass anything to postinstall script indicating if it's a debug build, + pythonwin_exe = os.path.join(lib_dir, "Pythonwin", "Pythonwin.exe") + pythonwin_edit_command = pythonwin_exe + ' -edit "%1"' + + keys_vals = [ + ( + "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Pythonwin.exe", + "", + pythonwin_exe, + ), + ( + "Software\\Classes\\Python.File\\shell\\Edit with Pythonwin", + "command", + pythonwin_edit_command, + ), + ( + "Software\\Classes\\Python.NoConFile\\shell\\Edit with Pythonwin", + "command", + pythonwin_edit_command, + ), + ] + + try: + if register: + for key, sub_key, val in keys_vals: + ## Since winreg only uses the character Api functions, this can fail if Python + ## is installed to a path containing non-ascii characters + hkey = winreg.CreateKey(classes_root, key) + if sub_key: + hkey = winreg.CreateKey(hkey, sub_key) + winreg.SetValueEx(hkey, None, 0, winreg.REG_SZ, val) + hkey.Close() + else: + for key, sub_key, val in keys_vals: + try: + if sub_key: + hkey = winreg.OpenKey(classes_root, key) + winreg.DeleteKey(hkey, sub_key) + hkey.Close() + winreg.DeleteKey(classes_root, key) + except OSError as why: + winerror = getattr(why, "winerror", why.errno) + if winerror != 2: # file not found + raise + finally: + # tell windows about the change + from win32com.shell import shell, shellcon + + shell.SHChangeNotify( + shellcon.SHCNE_ASSOCCHANGED, shellcon.SHCNF_IDLIST, None, None + ) + + +def get_shortcuts_folder(): + if get_root_hkey() == winreg.HKEY_LOCAL_MACHINE: + try: + fldr = get_special_folder_path("CSIDL_COMMON_PROGRAMS") + except OSError: + # No CSIDL_COMMON_PROGRAMS on this platform + fldr = get_special_folder_path("CSIDL_PROGRAMS") + else: + # non-admin install - always goes in this user's start menu. + fldr = get_special_folder_path("CSIDL_PROGRAMS") + + try: + install_group = winreg.QueryValue( + get_root_hkey(), root_key_name + "\\InstallPath\\InstallGroup" + ) + except OSError: + vi = sys.version_info + install_group = "Python %d.%d" % (vi[0], vi[1]) + return os.path.join(fldr, install_group) + + +# Get the system directory, which may be the Wow64 directory if we are a 32bit +# python on a 64bit OS. +def get_system_dir(): + import win32api # we assume this exists. + + try: + import pythoncom + import win32process + from win32com.shell import shell, shellcon + + try: + if win32process.IsWow64Process(): + return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEMX86) + return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEM) + except (pythoncom.com_error, win32process.error): + return win32api.GetSystemDirectory() + except ImportError: + return win32api.GetSystemDirectory() + + +def fixup_dbi(): + # We used to have a dbi.pyd with our .pyd files, but now have a .py file. + # If the user didn't uninstall, they will find the .pyd which will cause + # problems - so handle that. + import win32api + import win32con + + pyd_name = os.path.join(os.path.dirname(win32api.__file__), "dbi.pyd") + pyd_d_name = os.path.join(os.path.dirname(win32api.__file__), "dbi_d.pyd") + py_name = os.path.join(os.path.dirname(win32con.__file__), "dbi.py") + for this_pyd in (pyd_name, pyd_d_name): + this_dest = this_pyd + ".old" + if os.path.isfile(this_pyd) and os.path.isfile(py_name): + try: + if os.path.isfile(this_dest): + print( + "Old dbi '%s' already exists - deleting '%s'" + % (this_dest, this_pyd) + ) + os.remove(this_pyd) + else: + os.rename(this_pyd, this_dest) + print("renamed '%s'->'%s.old'" % (this_pyd, this_pyd)) + file_created(this_pyd + ".old") + except os.error as exc: + print("FAILED to rename '%s': %s" % (this_pyd, exc)) + + +def install(lib_dir): + import traceback + + # The .pth file is now installed as a regular file. + # Create the .pth file in the site-packages dir, and use only relative paths + # We used to write a .pth directly to sys.prefix - clobber it. + if os.path.isfile(os.path.join(sys.prefix, "pywin32.pth")): + os.unlink(os.path.join(sys.prefix, "pywin32.pth")) + # The .pth may be new and therefore not loaded in this session. + # Setup the paths just in case. + for name in "win32 win32\\lib Pythonwin".split(): + sys.path.append(os.path.join(lib_dir, name)) + # It is possible people with old versions installed with still have + # pywintypes and pythoncom registered. We no longer need this, and stale + # entries hurt us. + for name in "pythoncom pywintypes".split(): + keyname = "Software\\Python\\PythonCore\\" + sys.winver + "\\Modules\\" + name + for root in winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER: + try: + winreg.DeleteKey(root, keyname + "\\Debug") + except WindowsError: + pass + try: + winreg.DeleteKey(root, keyname) + except WindowsError: + pass + LoadSystemModule(lib_dir, "pywintypes") + LoadSystemModule(lib_dir, "pythoncom") + import win32api + + # and now we can get the system directory: + files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) + if not files: + raise RuntimeError("No system files to copy!!") + # Try the system32 directory first - if that fails due to "access denied", + # it implies a non-admin user, and we use sys.prefix + for dest_dir in [get_system_dir(), sys.prefix]: + # and copy some files over there + worked = 0 + try: + for fname in files: + base = os.path.basename(fname) + dst = os.path.join(dest_dir, base) + CopyTo("installing %s" % base, fname, dst) + if verbose: + print("Copied %s to %s" % (base, dst)) + # Register the files with the uninstaller + file_created(dst) + worked = 1 + # Nuke any other versions that may exist - having + # duplicates causes major headaches. + bad_dest_dirs = [ + os.path.join(sys.prefix, "Library\\bin"), + os.path.join(sys.prefix, "Lib\\site-packages\\win32"), + ] + if dest_dir != sys.prefix: + bad_dest_dirs.append(sys.prefix) + for bad_dest_dir in bad_dest_dirs: + bad_fname = os.path.join(bad_dest_dir, base) + if os.path.exists(bad_fname): + # let exceptions go here - delete must succeed + os.unlink(bad_fname) + if worked: + break + except win32api.error as details: + if details.winerror == 5: + # access denied - user not admin - try sys.prefix dir, + # but first check that a version doesn't already exist + # in that place - otherwise that one will still get used! + if os.path.exists(dst): + msg = ( + "The file '%s' exists, but can not be replaced " + "due to insufficient permissions. You must " + "reinstall this software as an Administrator" % dst + ) + print(msg) + raise RuntimeError(msg) + continue + raise + else: + raise RuntimeError( + "You don't have enough permissions to install the system files" + ) + + # Pythonwin 'compiles' config files - record them for uninstall. + pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") + for fname in glob.glob(os.path.join(pywin_dir, "*.cfg")): + file_created(fname[:-1] + "c") # .cfg->.cfc + + # Register our demo COM objects. + try: + try: + RegisterCOMObjects() + except win32api.error as details: + if details.winerror != 5: # ERROR_ACCESS_DENIED + raise + print("You do not have the permissions to install COM objects.") + print("The sample COM objects were not registered.") + except Exception: + print("FAILED to register the Python COM objects") + traceback.print_exc() + + # There may be no main Python key in HKCU if, eg, an admin installed + # python itself. + winreg.CreateKey(get_root_hkey(), root_key_name) + + chm_file = None + try: + chm_file = RegisterHelpFile(True, lib_dir) + except Exception: + print("Failed to register help file") + traceback.print_exc() + else: + if verbose: + print("Registered help file") + + # misc other fixups. + fixup_dbi() + + # Register Pythonwin in context menu + try: + RegisterPythonwin(True, lib_dir) + except Exception: + print("Failed to register pythonwin as editor") + traceback.print_exc() + else: + if verbose: + print("Pythonwin has been registered in context menu") + + # Create the win32com\gen_py directory. + make_dir = os.path.join(lib_dir, "win32com", "gen_py") + if not os.path.isdir(make_dir): + if verbose: + print("Creating directory %s" % (make_dir,)) + directory_created(make_dir) + os.mkdir(make_dir) + + try: + # create shortcuts + # CSIDL_COMMON_PROGRAMS only available works on NT/2000/XP, and + # will fail there if the user has no admin rights. + fldr = get_shortcuts_folder() + # If the group doesn't exist, then we don't make shortcuts - its + # possible that this isn't a "normal" install. + if os.path.isdir(fldr): + dst = os.path.join(fldr, "PythonWin.lnk") + create_shortcut( + os.path.join(lib_dir, "Pythonwin\\Pythonwin.exe"), + "The Pythonwin IDE", + dst, + "", + sys.prefix, + ) + file_created(dst) + if verbose: + print("Shortcut for Pythonwin created") + # And the docs. + if chm_file: + dst = os.path.join(fldr, "Python for Windows Documentation.lnk") + doc = "Documentation for the PyWin32 extensions" + create_shortcut(chm_file, doc, dst) + file_created(dst) + if verbose: + print("Shortcut to documentation created") + else: + if verbose: + print("Can't install shortcuts - %r is not a folder" % (fldr,)) + except Exception as details: + print(details) + + # importing win32com.client ensures the gen_py dir created - not strictly + # necessary to do now, but this makes the installation "complete" + try: + import win32com.client # noqa + except ImportError: + # Don't let this error sound fatal + pass + print("The pywin32 extensions were successfully installed.") + + if is_bdist_wininst: + # Open a web page with info about the .exe installers being deprecated. + import webbrowser + + try: + webbrowser.open("https://mhammond.github.io/pywin32_installers.html") + except webbrowser.Error: + print("Please visit https://mhammond.github.io/pywin32_installers.html") + + +def uninstall(lib_dir): + # First ensure our system modules are loaded from pywin32_system, so + # we can remove the ones we copied... + LoadSystemModule(lib_dir, "pywintypes") + LoadSystemModule(lib_dir, "pythoncom") + + try: + RegisterCOMObjects(False) + except Exception as why: + print("Failed to unregister COM objects: %s" % (why,)) + + try: + RegisterHelpFile(False, lib_dir) + except Exception as why: + print("Failed to unregister help file: %s" % (why,)) + else: + if verbose: + print("Unregistered help file") + + try: + RegisterPythonwin(False, lib_dir) + except Exception as why: + print("Failed to unregister Pythonwin: %s" % (why,)) + else: + if verbose: + print("Unregistered Pythonwin") + + try: + # remove gen_py directory. + gen_dir = os.path.join(lib_dir, "win32com", "gen_py") + if os.path.isdir(gen_dir): + shutil.rmtree(gen_dir) + if verbose: + print("Removed directory %s" % (gen_dir,)) + + # Remove pythonwin compiled "config" files. + pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") + for fname in glob.glob(os.path.join(pywin_dir, "*.cfc")): + os.remove(fname) + + # The dbi.pyd.old files we may have created. + try: + os.remove(os.path.join(lib_dir, "win32", "dbi.pyd.old")) + except os.error: + pass + try: + os.remove(os.path.join(lib_dir, "win32", "dbi_d.pyd.old")) + except os.error: + pass + + except Exception as why: + print("Failed to remove misc files: %s" % (why,)) + + try: + fldr = get_shortcuts_folder() + for link in ("PythonWin.lnk", "Python for Windows Documentation.lnk"): + fqlink = os.path.join(fldr, link) + if os.path.isfile(fqlink): + os.remove(fqlink) + if verbose: + print("Removed %s" % (link,)) + except Exception as why: + print("Failed to remove shortcuts: %s" % (why,)) + # Now remove the system32 files. + files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) + # Try the system32 directory first - if that fails due to "access denied", + # it implies a non-admin user, and we use sys.prefix + try: + for dest_dir in [get_system_dir(), sys.prefix]: + # and copy some files over there + worked = 0 + for fname in files: + base = os.path.basename(fname) + dst = os.path.join(dest_dir, base) + if os.path.isfile(dst): + try: + os.remove(dst) + worked = 1 + if verbose: + print("Removed file %s" % (dst)) + except Exception: + print("FAILED to remove %s" % (dst,)) + if worked: + break + except Exception as why: + print("FAILED to remove system files: %s" % (why,)) + + +# NOTE: If this script is run from inside the bdist_wininst created +# binary installer or uninstaller, the command line args are either +# '-install' or '-remove'. + +# Important: From inside the binary installer this script MUST NOT +# call sys.exit() or raise SystemExit, otherwise not only this script +# but also the installer will terminate! (Is there a way to prevent +# this from the bdist_wininst C code?) + + +def verify_destination(location): + if not os.path.isdir(location): + raise argparse.ArgumentTypeError('Path "{}" does not exist!'.format(location)) + return location + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""A post-install script for the pywin32 extensions. + + * Typical usage: + + > python pywin32_postinstall.py -install + + If you installed pywin32 via a .exe installer, this should be run + automatically after installation, but if it fails you can run it again. + + If you installed pywin32 via PIP, you almost certainly need to run this to + setup the environment correctly. + + Execute with script with a '-install' parameter, to ensure the environment + is setup correctly. + """, + ) + parser.add_argument( + "-install", + default=False, + action="store_true", + help="Configure the Python environment correctly for pywin32.", + ) + parser.add_argument( + "-remove", + default=False, + action="store_true", + help="Try and remove everything that was installed or copied.", + ) + parser.add_argument( + "-wait", + type=int, + help="Wait for the specified process to terminate before starting.", + ) + parser.add_argument( + "-silent", + default=False, + action="store_true", + help='Don\'t display the "Abort/Retry/Ignore" dialog for files in use.', + ) + parser.add_argument( + "-quiet", + default=False, + action="store_true", + help="Don't display progress messages.", + ) + parser.add_argument( + "-destination", + default=sysconfig.get_paths()["platlib"], + type=verify_destination, + help="Location of the PyWin32 installation", + ) + + args = parser.parse_args() + + if not args.quiet: + print("Parsed arguments are: {}".format(args)) + + if not args.install ^ args.remove: + parser.error("You need to either choose to -install or -remove!") + + if args.wait is not None: + try: + os.waitpid(args.wait, 0) + except os.error: + # child already dead + pass + + silent = args.silent + verbose = not args.quiet + + if args.install: + install(args.destination) + + if args.remove: + if not is_bdist_wininst: + uninstall(args.destination) + + +if __name__ == "__main__": + main() diff --git a/lab_3/aimenv/Scripts/pywin32_testall.py b/lab_3/aimenv/Scripts/pywin32_testall.py new file mode 100644 index 0000000..a54f9d4 --- /dev/null +++ b/lab_3/aimenv/Scripts/pywin32_testall.py @@ -0,0 +1,124 @@ +"""A test runner for pywin32""" +import os +import site +import subprocess +import sys + +# locate the dirs based on where this script is - it may be either in the +# source tree, or in an installed Python 'Scripts' tree. +this_dir = os.path.dirname(__file__) +site_packages = [ + site.getusersitepackages(), +] + site.getsitepackages() + +failures = [] + + +# Run a test using subprocess and wait for the result. +# If we get an returncode != 0, we know that there was an error, but we don't +# abort immediately - we run as many tests as we can. +def run_test(script, cmdline_extras): + dirname, scriptname = os.path.split(script) + # some tests prefer to be run from their directory. + cmd = [sys.executable, "-u", scriptname] + cmdline_extras + print("--- Running '%s' ---" % script) + sys.stdout.flush() + result = subprocess.run(cmd, check=False, cwd=dirname) + print("*** Test script '%s' exited with %s" % (script, result.returncode)) + sys.stdout.flush() + if result.returncode: + failures.append(script) + + +def find_and_run(possible_locations, extras): + for maybe in possible_locations: + if os.path.isfile(maybe): + run_test(maybe, extras) + break + else: + raise RuntimeError( + "Failed to locate a test script in one of %s" % possible_locations + ) + + +def main(): + import argparse + + code_directories = [this_dir] + site_packages + + parser = argparse.ArgumentParser( + description="A script to trigger tests in all subprojects of PyWin32." + ) + parser.add_argument( + "-no-user-interaction", + default=False, + action="store_true", + help="(This is now the default - use `-user-interaction` to include them)", + ) + + parser.add_argument( + "-user-interaction", + action="store_true", + help="Include tests which require user interaction", + ) + + parser.add_argument( + "-skip-adodbapi", + default=False, + action="store_true", + help="Skip the adodbapi tests; useful for CI where there's no provider", + ) + + args, remains = parser.parse_known_args() + + # win32, win32ui / Pythonwin + + extras = [] + if args.user_interaction: + extras += ["-user-interaction"] + extras.extend(remains) + scripts = [ + "win32/test/testall.py", + "Pythonwin/pywin/test/all.py", + ] + for script in scripts: + maybes = [os.path.join(directory, script) for directory in code_directories] + find_and_run(maybes, extras) + + # win32com + maybes = [ + os.path.join(directory, "win32com", "test", "testall.py") + for directory in [ + os.path.join(this_dir, "com"), + ] + + site_packages + ] + extras = remains + ["1"] # only run "level 1" tests in CI + find_and_run(maybes, extras) + + # adodbapi + if not args.skip_adodbapi: + maybes = [ + os.path.join(directory, "adodbapi", "test", "adodbapitest.py") + for directory in code_directories + ] + find_and_run(maybes, remains) + # This script has a hard-coded sql server name in it, (and markh typically + # doesn't have a different server to test on) but there is now supposed to be a server out there on the Internet + # just to run these tests, so try it... + maybes = [ + os.path.join(directory, "adodbapi", "test", "test_adodbapi_dbapi20.py") + for directory in code_directories + ] + find_and_run(maybes, remains) + + if failures: + print("The following scripts failed") + for failure in failures: + print(">", failure) + sys.exit(1) + print("All tests passed \\o/") + + +if __name__ == "__main__": + main() diff --git a/lab_3/aimenv/pyvenv.cfg b/lab_3/aimenv/pyvenv.cfg new file mode 100644 index 0000000..b18a31c --- /dev/null +++ b/lab_3/aimenv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = C:\Users\Egor\AppData\Local\Programs\Python\Python312 +include-system-site-packages = false +version = 3.12.5 +executable = C:\Users\Egor\AppData\Local\Programs\Python\Python312\python.exe +command = C:\Users\Egor\AppData\Local\Programs\Python\Python312\python.exe -m venv C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv diff --git a/lab_3/aimenv/share/jupyter/kernels/python3/kernel.json b/lab_3/aimenv/share/jupyter/kernels/python3/kernel.json new file mode 100644 index 0000000..cca38a4 --- /dev/null +++ b/lab_3/aimenv/share/jupyter/kernels/python3/kernel.json @@ -0,0 +1,14 @@ +{ + "argv": [ + "python", + "-m", + "ipykernel_launcher", + "-f", + "{connection_file}" + ], + "display_name": "Python 3 (ipykernel)", + "language": "python", + "metadata": { + "debugger": true + } +} \ No newline at end of file diff --git a/lab_3/aimenv/share/jupyter/kernels/python3/logo-32x32.png b/lab_3/aimenv/share/jupyter/kernels/python3/logo-32x32.png new file mode 100644 index 0000000..be81330 Binary files /dev/null and b/lab_3/aimenv/share/jupyter/kernels/python3/logo-32x32.png differ diff --git a/lab_3/aimenv/share/jupyter/kernels/python3/logo-64x64.png b/lab_3/aimenv/share/jupyter/kernels/python3/logo-64x64.png new file mode 100644 index 0000000..eebbff6 Binary files /dev/null and b/lab_3/aimenv/share/jupyter/kernels/python3/logo-64x64.png differ diff --git a/lab_3/aimenv/share/jupyter/kernels/python3/logo-svg.svg b/lab_3/aimenv/share/jupyter/kernels/python3/logo-svg.svg new file mode 100644 index 0000000..467b07b --- /dev/null +++ b/lab_3/aimenv/share/jupyter/kernels/python3/logo-svg.svg @@ -0,0 +1,265 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab_3/aimenv/share/man/man1/ipython.1 b/lab_3/aimenv/share/man/man1/ipython.1 new file mode 100644 index 0000000..0f4a191 --- /dev/null +++ b/lab_3/aimenv/share/man/man1/ipython.1 @@ -0,0 +1,60 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH IPYTHON 1 "July 15, 2011" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) and groff_man(7) +.\" .SH section heading +.\" .SS secondary section heading +.\" +.\" +.\" To preview this page as plain text: nroff -man ipython.1 +.\" +.SH NAME +ipython \- Tools for Interactive Computing in Python. +.SH SYNOPSIS +.B ipython +.RI [ options ] " files" ... + +.B ipython subcommand +.RI [ options ] ... + +.SH DESCRIPTION +An interactive Python shell with automatic history (input and output), dynamic +object introspection, easier configuration, command completion, access to the +system shell, integration with numerical and scientific computing tools, +web notebook, Qt console, and more. + +For more information on how to use IPython, see 'ipython \-\-help', +or 'ipython \-\-help\-all' for all available command\(hyline options. + +.SH "ENVIRONMENT VARIABLES" +.sp +.PP +\fIIPYTHONDIR\fR +.RS 4 +This is the location where IPython stores all its configuration files. The default +is $HOME/.ipython if IPYTHONDIR is not defined. + +You can see the computed value of IPYTHONDIR with `ipython locate`. + +.SH FILES + +IPython uses various configuration files stored in profiles within IPYTHONDIR. +To generate the default configuration files and start configuring IPython, +do 'ipython profile create', and edit '*_config.py' files located in +IPYTHONDIR/profile_default. + +.SH AUTHORS +IPython is written by the IPython Development Team . diff --git a/lab_4/aimenv/Scripts/Activate.ps1 b/lab_4/aimenv/Scripts/Activate.ps1 new file mode 100644 index 0000000..3045241 --- /dev/null +++ b/lab_4/aimenv/Scripts/Activate.ps1 @@ -0,0 +1,502 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" + +# SIG # Begin signature block +# MIIvIwYJKoZIhvcNAQcCoIIvFDCCLxACAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk +# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h +# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z +# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z +# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ +# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s +# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL +# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb +# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 +# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c +# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx +# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 +# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL +# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud +# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf +# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk +# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS +# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK +# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB +# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp +# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg +# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri +# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 +# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 +# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 +# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H +# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G +# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C +# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce +# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da +# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T +# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA +# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh +# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM +# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z +# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 +# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY +# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP +# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T +# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD +# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG +# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY +# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj +# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV +# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN +# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry +# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL +# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf +# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh +# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh +# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV +# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j +# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH +# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC +# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l +# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW +# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw +# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x +# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ +# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7 +# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx +# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb +# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA +# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm +# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA +# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1 +# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc +# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w +# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B +# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj +# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E +# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM +# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI +# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v +# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex +# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v +# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF +# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6 +# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu +# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI +# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N +# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA +# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2 +# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O +# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd +# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50 +# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU +# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq +# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs +# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa +# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa +# tjCCGrICAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT +# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl +# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC +# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62 +# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA +# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAyAC4ANQBfADIAMAAyADQA +# MAA4ADAANgAuADAAMaECgAAwDQYJKoZIhvcNAQEBBQAEggIAoXbLeBCFQhwr4rTK +# R0WSySG7AtpuY1n5vhwkJPE0JgQ11PFJYphroU2ouWWM8ifejqa6m21JEWGjC9En +# Rpzpe1+eps7ClsdO+y5NxZc/3vD1j7IddJdzZh77QqDFMqJEeDNY+00OxxnnhbN1 +# wJk29w8qRyIJ7HpCM0E5b8R8Atooip5ihAgrdrIsyyA3Mnl5Y+YMdqtQYe4QtOhE +# QcEoxAMoI5nLSGsbLhEM8CArl36EmX31eHTVMRJMaM98p0DkURHL030ALmW2V70h +# M7ovmhOezFyndR1d3HtcfwRB3nr5vHWZe6ythZ3wVgpsN++RdDOvHjb9LC9lkth/ +# BGbcmVqsA9ZHnub1iPt89GsQBSiXjaOnWUxgJi0Qd3s2pwswLxHp05QDUE/d8EF7 +# Wy6aNPI43+G2BjPLVeM3iVbMWd/yxhH6pddaVPAMKVvxJoJ7PfDLihMNyonHt0on +# xuaM5r2KaVMWpHIkgLiB9tyvdIQb0IW+YU05VAnOqh7CDaEtP7jM6P0usxY9ufEC +# BFZnOGb3M/c4KbcOuHOIkY3jGqw+DLZFrcWiIe2wbi2TsXDixs+pz8vm/KQczrQ2 +# RJ1R8jrbK7IIRyZmTYf+dStZG3NhNQn1xcPYraHKNOm9CzNmeXJTdfAe0BEApqUN +# 9AiLj6uvSEp278ysr/EE3ayw2Qmhghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcn +# BgkqhkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3 +# DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgpuSq +# fyINa45wSs5Sa6msoQk+zCLDcSK24OqaBM/0/2cCEFtb0VJATq3jxU9l7ewmqjcY +# DzIwMjQwODA2MjEwMDM5WqCCEwkwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f +# 5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp +# Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 +# IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEz +# MjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x +# IDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0B +# AQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6 +# OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp +# 52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF +# 6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G4 +# 5lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7p +# Be6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAs +# NJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU +# 6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwK +# WEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFx +# smxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbR +# yV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPL +# QHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG +# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG +# SAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4E +# FgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov +# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1 +# NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUH +# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDov +# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNI +# QTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCp +# tZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX +# 4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoF +# eoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+nji +# kxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBB +# Jt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJ +# A+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcs +# QdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE +# 5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS +# 2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3 +# CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUb +# c7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqADAgECAhAHNje3JFR8 +# 2Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0z +# NzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg +# SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1 +# NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +# AQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI +# 82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9 +# xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ +# 3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5Emfv +# DqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDET +# qVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHe +# IhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jo +# n7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ +# 9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/T +# Xkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJg +# o1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkw +# EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+e +# yG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQD +# AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF +# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw +# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy +# dDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln +# aUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg +# hkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGw +# GC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0 +# MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1D +# X+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw +# 1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY +# +/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0I +# SQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr +# 5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7y +# Rp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDop +# hrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/ +# AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMO +# Hds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkq +# hkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j +# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB +# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5 +# WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1K +# PDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2r +# snnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C +# 8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBf +# sXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +# QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8 +# rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaY +# dj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+ +# wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw +# ++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+N +# P8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7F +# wI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUw +# AwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAU +# Reuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEB +# BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG +# AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 +# cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAow +# CDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/ +# Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLe +# JLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE +# 1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9Hda +# XFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbO +# byMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYID +# djCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYg +# VGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUA +# oIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcN +# MjQwODA2MjEwMDM5WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4Jd +# zqqKycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQglCIBxGudJQwqEBh+XAoT3nqSoAuS +# uMjmJTX95zFjdk0wNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBX +# aEE2b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIAOkILAZviyFOU +# Qzt10RYNFHl0zO4rgXcR5oCeJlU1n9y+DwjCTvcrax9qdkEuiEJWDewXbak3TPQK +# 0ts7jhUIFMDTEn8GZXysruzDlYNLstKM4RbYIK+f2772phehvABS5mn70+L63GXe +# A5UFYM5M7BAvEY+3DKEwUnN9lAl8YKi1xS545MXYm1B96gI/7oEBDkNV2DoNIZAw +# R2B4wPTcpI2aG5zZ0jFgVtq8bOXLZ9b9pBrhKbf4PZWxPqAFwUtZryQKdt770u3Y +# l0WR2SgemKq4aOEvajD1J4fC56lnUoekXt4yH8/fBueCXYx+ADoEkU4/ota7C1oL +# aCZE4G0iQOH9XFtMUjA87oEPisJG63onir6tsurTjjm/wK8VnFQBSii4ILtfSOfR +# kDMsu7kS0H5SWliY3sPlDTn4Kwl14EThMmyXUr7SFFHnsibHtfLATTmV6XyeJ03l +# BmwDl8hdzt5G0pjH/u3bTFcdJu7J0RQuGYgpmNsVYjHCQnZDrJjzIE2os/QYgL6D +# B/ZYSv96jnYs6cFd93R0ixZMsQPQKcs2gbVYz3nymJL7t605LzW86tENmORsUdgm +# qh0ky+qe/+D/f88WLLjdHi/xfskiFKEL66Y4EWkECoUUMBRcJlIg1GszTCVmwD1N +# foIJo8CaFGMoR+QHwDeamNbOOlrCFMQ= +# SIG # End signature block diff --git a/lab_4/aimenv/Scripts/activate b/lab_4/aimenv/Scripts/activate new file mode 100644 index 0000000..8cf30bf --- /dev/null +++ b/lab_4/aimenv/Scripts/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath "C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv") +else + # use the path as-is + export VIRTUAL_ENV="C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv" +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/Scripts:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(aimenv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(aimenv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/lab_4/aimenv/Scripts/activate.bat b/lab_4/aimenv/Scripts/activate.bat new file mode 100644 index 0000000..11bc012 --- /dev/null +++ b/lab_4/aimenv/Scripts/activate.bat @@ -0,0 +1,34 @@ +@echo off + +rem This file is UTF-8 encoded, so we need to update the current code page while executing it +for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( + set _OLD_CODEPAGE=%%a +) +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul +) + +set VIRTUAL_ENV=C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv + +if not defined PROMPT set PROMPT=$P$G + +if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% +if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% + +set _OLD_VIRTUAL_PROMPT=%PROMPT% +set PROMPT=(aimenv) %PROMPT% + +if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% +set PYTHONHOME= + +if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% +if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% + +set PATH=%VIRTUAL_ENV%\Scripts;%PATH% +set VIRTUAL_ENV_PROMPT=(aimenv) + +:END +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + set _OLD_CODEPAGE= +) diff --git a/lab_4/aimenv/Scripts/deactivate.bat b/lab_4/aimenv/Scripts/deactivate.bat new file mode 100644 index 0000000..62a39a7 --- /dev/null +++ b/lab_4/aimenv/Scripts/deactivate.bat @@ -0,0 +1,22 @@ +@echo off + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) +set _OLD_VIRTUAL_PROMPT= + +if defined _OLD_VIRTUAL_PYTHONHOME ( + set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + set _OLD_VIRTUAL_PYTHONHOME= +) + +if defined _OLD_VIRTUAL_PATH ( + set "PATH=%_OLD_VIRTUAL_PATH%" +) + +set _OLD_VIRTUAL_PATH= + +set VIRTUAL_ENV= +set VIRTUAL_ENV_PROMPT= + +:END diff --git a/lab_4/aimenv/Scripts/debugpy.exe b/lab_4/aimenv/Scripts/debugpy.exe new file mode 100644 index 0000000..6d0e480 Binary files /dev/null and b/lab_4/aimenv/Scripts/debugpy.exe differ diff --git a/lab_4/aimenv/Scripts/ipython.exe b/lab_4/aimenv/Scripts/ipython.exe new file mode 100644 index 0000000..c5d0b33 Binary files /dev/null and b/lab_4/aimenv/Scripts/ipython.exe differ diff --git a/lab_4/aimenv/Scripts/ipython3.exe b/lab_4/aimenv/Scripts/ipython3.exe new file mode 100644 index 0000000..c5d0b33 Binary files /dev/null and b/lab_4/aimenv/Scripts/ipython3.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter-kernel.exe b/lab_4/aimenv/Scripts/jupyter-kernel.exe new file mode 100644 index 0000000..cc1b7f4 Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter-kernel.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter-kernelspec.exe b/lab_4/aimenv/Scripts/jupyter-kernelspec.exe new file mode 100644 index 0000000..72bf4e7 Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter-kernelspec.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter-migrate.exe b/lab_4/aimenv/Scripts/jupyter-migrate.exe new file mode 100644 index 0000000..2033e15 Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter-migrate.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter-run.exe b/lab_4/aimenv/Scripts/jupyter-run.exe new file mode 100644 index 0000000..1d34b22 Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter-run.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter-troubleshoot.exe b/lab_4/aimenv/Scripts/jupyter-troubleshoot.exe new file mode 100644 index 0000000..de9c4d6 Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter-troubleshoot.exe differ diff --git a/lab_4/aimenv/Scripts/jupyter.exe b/lab_4/aimenv/Scripts/jupyter.exe new file mode 100644 index 0000000..77e316a Binary files /dev/null and b/lab_4/aimenv/Scripts/jupyter.exe differ diff --git a/lab_4/aimenv/Scripts/pip.exe b/lab_4/aimenv/Scripts/pip.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_4/aimenv/Scripts/pip.exe differ diff --git a/lab_4/aimenv/Scripts/pip3.12.exe b/lab_4/aimenv/Scripts/pip3.12.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_4/aimenv/Scripts/pip3.12.exe differ diff --git a/lab_4/aimenv/Scripts/pip3.exe b/lab_4/aimenv/Scripts/pip3.exe new file mode 100644 index 0000000..e378251 Binary files /dev/null and b/lab_4/aimenv/Scripts/pip3.exe differ diff --git a/lab_4/aimenv/Scripts/pygmentize.exe b/lab_4/aimenv/Scripts/pygmentize.exe new file mode 100644 index 0000000..9c1459b Binary files /dev/null and b/lab_4/aimenv/Scripts/pygmentize.exe differ diff --git a/lab_4/aimenv/Scripts/python.exe b/lab_4/aimenv/Scripts/python.exe new file mode 100644 index 0000000..b58faef Binary files /dev/null and b/lab_4/aimenv/Scripts/python.exe differ diff --git a/lab_4/aimenv/Scripts/pythonw.exe b/lab_4/aimenv/Scripts/pythonw.exe new file mode 100644 index 0000000..ca33b90 Binary files /dev/null and b/lab_4/aimenv/Scripts/pythonw.exe differ diff --git a/lab_4/aimenv/Scripts/pywin32_postinstall.py b/lab_4/aimenv/Scripts/pywin32_postinstall.py new file mode 100644 index 0000000..147f0cd --- /dev/null +++ b/lab_4/aimenv/Scripts/pywin32_postinstall.py @@ -0,0 +1,783 @@ +# postinstall script for pywin32 +# +# copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory, +# and creates a pth file +import glob +import os +import shutil +import sys +import sysconfig + +try: + import winreg as winreg +except: + import winreg + +# Send output somewhere so it can be found if necessary... +import tempfile + +tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w") + + +class Tee: + def __init__(self, file): + self.f = file + + def write(self, what): + if self.f is not None: + try: + self.f.write(what.replace("\n", "\r\n")) + except IOError: + pass + tee_f.write(what) + + def flush(self): + if self.f is not None: + try: + self.f.flush() + except IOError: + pass + tee_f.flush() + + +# For some unknown reason, when running under bdist_wininst we will start up +# with sys.stdout as None but stderr is hooked up. This work-around allows +# bdist_wininst to see the output we write and display it at the end of +# the install. +if sys.stdout is None: + sys.stdout = sys.stderr + +sys.stderr = Tee(sys.stderr) +sys.stdout = Tee(sys.stdout) + +com_modules = [ + # module_name, class_names + ("win32com.servers.interp", "Interpreter"), + ("win32com.servers.dictionary", "DictionaryPolicy"), + ("win32com.axscript.client.pyscript", "PyScript"), +] + +# Is this a 'silent' install - ie, avoid all dialogs. +# Different than 'verbose' +silent = 0 + +# Verbosity of output messages. +verbose = 1 + +root_key_name = "Software\\Python\\PythonCore\\" + sys.winver + +try: + # When this script is run from inside the bdist_wininst installer, + # file_created() and directory_created() are additional builtin + # functions which write lines to Python23\pywin32-install.log. This is + # a list of actions for the uninstaller, the format is inspired by what + # the Wise installer also creates. + file_created + is_bdist_wininst = True +except NameError: + is_bdist_wininst = False # we know what it is not - but not what it is :) + + def file_created(file): + pass + + def directory_created(directory): + pass + + def get_root_hkey(): + try: + winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY + ) + return winreg.HKEY_LOCAL_MACHINE + except OSError: + # Either not exist, or no permissions to create subkey means + # must be HKCU + return winreg.HKEY_CURRENT_USER + + +try: + create_shortcut +except NameError: + # Create a function with the same signature as create_shortcut provided + # by bdist_wininst + def create_shortcut( + path, description, filename, arguments="", workdir="", iconpath="", iconindex=0 + ): + import pythoncom + from win32com.shell import shell + + ilink = pythoncom.CoCreateInstance( + shell.CLSID_ShellLink, + None, + pythoncom.CLSCTX_INPROC_SERVER, + shell.IID_IShellLink, + ) + ilink.SetPath(path) + ilink.SetDescription(description) + if arguments: + ilink.SetArguments(arguments) + if workdir: + ilink.SetWorkingDirectory(workdir) + if iconpath or iconindex: + ilink.SetIconLocation(iconpath, iconindex) + # now save it. + ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile) + ipf.Save(filename, 0) + + # Support the same list of "path names" as bdist_wininst. + def get_special_folder_path(path_name): + from win32com.shell import shell, shellcon + + for maybe in """ + CSIDL_COMMON_STARTMENU CSIDL_STARTMENU CSIDL_COMMON_APPDATA + CSIDL_LOCAL_APPDATA CSIDL_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY + CSIDL_DESKTOPDIRECTORY CSIDL_COMMON_STARTUP CSIDL_STARTUP + CSIDL_COMMON_PROGRAMS CSIDL_PROGRAMS CSIDL_PROGRAM_FILES_COMMON + CSIDL_PROGRAM_FILES CSIDL_FONTS""".split(): + if maybe == path_name: + csidl = getattr(shellcon, maybe) + return shell.SHGetSpecialFolderPath(0, csidl, False) + raise ValueError("%s is an unknown path ID" % (path_name,)) + + +def CopyTo(desc, src, dest): + import win32api + import win32con + + while 1: + try: + win32api.CopyFile(src, dest, 0) + return + except win32api.error as details: + if details.winerror == 5: # access denied - user not admin. + raise + if silent: + # Running silent mode - just re-raise the error. + raise + full_desc = ( + "Error %s\n\n" + "If you have any Python applications running, " + "please close them now\nand select 'Retry'\n\n%s" + % (desc, details.strerror) + ) + rc = win32api.MessageBox( + 0, full_desc, "Installation Error", win32con.MB_ABORTRETRYIGNORE + ) + if rc == win32con.IDABORT: + raise + elif rc == win32con.IDIGNORE: + return + # else retry - around we go again. + + +# We need to import win32api to determine the Windows system directory, +# so we can copy our system files there - but importing win32api will +# load the pywintypes.dll already in the system directory preventing us +# from updating them! +# So, we pull the same trick pywintypes.py does, but it loads from +# our pywintypes_system32 directory. +def LoadSystemModule(lib_dir, modname): + # See if this is a debug build. + import importlib.machinery + import importlib.util + + suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" + filename = "%s%d%d%s.dll" % ( + modname, + sys.version_info[0], + sys.version_info[1], + suffix, + ) + filename = os.path.join(lib_dir, "pywin32_system32", filename) + loader = importlib.machinery.ExtensionFileLoader(modname, filename) + spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + +def SetPyKeyVal(key_name, value_name, value): + root_hkey = get_root_hkey() + root_key = winreg.OpenKey(root_hkey, root_key_name) + try: + my_key = winreg.CreateKey(root_key, key_name) + try: + winreg.SetValueEx(my_key, value_name, 0, winreg.REG_SZ, value) + if verbose: + print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value)) + finally: + my_key.Close() + finally: + root_key.Close() + + +def UnsetPyKeyVal(key_name, value_name, delete_key=False): + root_hkey = get_root_hkey() + root_key = winreg.OpenKey(root_hkey, root_key_name) + try: + my_key = winreg.OpenKey(root_key, key_name, 0, winreg.KEY_SET_VALUE) + try: + winreg.DeleteValue(my_key, value_name) + if verbose: + print("-> DELETE %s\\%s[%s]" % (root_key_name, key_name, value_name)) + finally: + my_key.Close() + if delete_key: + winreg.DeleteKey(root_key, key_name) + if verbose: + print("-> DELETE %s\\%s" % (root_key_name, key_name)) + except OSError as why: + winerror = getattr(why, "winerror", why.errno) + if winerror != 2: # file not found + raise + finally: + root_key.Close() + + +def RegisterCOMObjects(register=True): + import win32com.server.register + + if register: + func = win32com.server.register.RegisterClasses + else: + func = win32com.server.register.UnregisterClasses + flags = {} + if not verbose: + flags["quiet"] = 1 + for module, klass_name in com_modules: + __import__(module) + mod = sys.modules[module] + flags["finalize_register"] = getattr(mod, "DllRegisterServer", None) + flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None) + klass = getattr(mod, klass_name) + func(klass, **flags) + + +def RegisterHelpFile(register=True, lib_dir=None): + if lib_dir is None: + lib_dir = sysconfig.get_paths()["platlib"] + if register: + # Register the .chm help file. + chm_file = os.path.join(lib_dir, "PyWin32.chm") + if os.path.isfile(chm_file): + # This isn't recursive, so if 'Help' doesn't exist, we croak + SetPyKeyVal("Help", None, None) + SetPyKeyVal("Help\\Pythonwin Reference", None, chm_file) + return chm_file + else: + print("NOTE: PyWin32.chm can not be located, so has not " "been registered") + else: + UnsetPyKeyVal("Help\\Pythonwin Reference", None, delete_key=True) + return None + + +def RegisterPythonwin(register=True, lib_dir=None): + """Add (or remove) Pythonwin to context menu for python scripts. + ??? Should probably also add Edit command for pys files also. + Also need to remove these keys on uninstall, but there's no function + like file_created to add registry entries to uninstall log ??? + """ + import os + + if lib_dir is None: + lib_dir = sysconfig.get_paths()["platlib"] + classes_root = get_root_hkey() + ## Installer executable doesn't seem to pass anything to postinstall script indicating if it's a debug build, + pythonwin_exe = os.path.join(lib_dir, "Pythonwin", "Pythonwin.exe") + pythonwin_edit_command = pythonwin_exe + ' -edit "%1"' + + keys_vals = [ + ( + "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Pythonwin.exe", + "", + pythonwin_exe, + ), + ( + "Software\\Classes\\Python.File\\shell\\Edit with Pythonwin", + "command", + pythonwin_edit_command, + ), + ( + "Software\\Classes\\Python.NoConFile\\shell\\Edit with Pythonwin", + "command", + pythonwin_edit_command, + ), + ] + + try: + if register: + for key, sub_key, val in keys_vals: + ## Since winreg only uses the character Api functions, this can fail if Python + ## is installed to a path containing non-ascii characters + hkey = winreg.CreateKey(classes_root, key) + if sub_key: + hkey = winreg.CreateKey(hkey, sub_key) + winreg.SetValueEx(hkey, None, 0, winreg.REG_SZ, val) + hkey.Close() + else: + for key, sub_key, val in keys_vals: + try: + if sub_key: + hkey = winreg.OpenKey(classes_root, key) + winreg.DeleteKey(hkey, sub_key) + hkey.Close() + winreg.DeleteKey(classes_root, key) + except OSError as why: + winerror = getattr(why, "winerror", why.errno) + if winerror != 2: # file not found + raise + finally: + # tell windows about the change + from win32com.shell import shell, shellcon + + shell.SHChangeNotify( + shellcon.SHCNE_ASSOCCHANGED, shellcon.SHCNF_IDLIST, None, None + ) + + +def get_shortcuts_folder(): + if get_root_hkey() == winreg.HKEY_LOCAL_MACHINE: + try: + fldr = get_special_folder_path("CSIDL_COMMON_PROGRAMS") + except OSError: + # No CSIDL_COMMON_PROGRAMS on this platform + fldr = get_special_folder_path("CSIDL_PROGRAMS") + else: + # non-admin install - always goes in this user's start menu. + fldr = get_special_folder_path("CSIDL_PROGRAMS") + + try: + install_group = winreg.QueryValue( + get_root_hkey(), root_key_name + "\\InstallPath\\InstallGroup" + ) + except OSError: + vi = sys.version_info + install_group = "Python %d.%d" % (vi[0], vi[1]) + return os.path.join(fldr, install_group) + + +# Get the system directory, which may be the Wow64 directory if we are a 32bit +# python on a 64bit OS. +def get_system_dir(): + import win32api # we assume this exists. + + try: + import pythoncom + import win32process + from win32com.shell import shell, shellcon + + try: + if win32process.IsWow64Process(): + return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEMX86) + return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEM) + except (pythoncom.com_error, win32process.error): + return win32api.GetSystemDirectory() + except ImportError: + return win32api.GetSystemDirectory() + + +def fixup_dbi(): + # We used to have a dbi.pyd with our .pyd files, but now have a .py file. + # If the user didn't uninstall, they will find the .pyd which will cause + # problems - so handle that. + import win32api + import win32con + + pyd_name = os.path.join(os.path.dirname(win32api.__file__), "dbi.pyd") + pyd_d_name = os.path.join(os.path.dirname(win32api.__file__), "dbi_d.pyd") + py_name = os.path.join(os.path.dirname(win32con.__file__), "dbi.py") + for this_pyd in (pyd_name, pyd_d_name): + this_dest = this_pyd + ".old" + if os.path.isfile(this_pyd) and os.path.isfile(py_name): + try: + if os.path.isfile(this_dest): + print( + "Old dbi '%s' already exists - deleting '%s'" + % (this_dest, this_pyd) + ) + os.remove(this_pyd) + else: + os.rename(this_pyd, this_dest) + print("renamed '%s'->'%s.old'" % (this_pyd, this_pyd)) + file_created(this_pyd + ".old") + except os.error as exc: + print("FAILED to rename '%s': %s" % (this_pyd, exc)) + + +def install(lib_dir): + import traceback + + # The .pth file is now installed as a regular file. + # Create the .pth file in the site-packages dir, and use only relative paths + # We used to write a .pth directly to sys.prefix - clobber it. + if os.path.isfile(os.path.join(sys.prefix, "pywin32.pth")): + os.unlink(os.path.join(sys.prefix, "pywin32.pth")) + # The .pth may be new and therefore not loaded in this session. + # Setup the paths just in case. + for name in "win32 win32\\lib Pythonwin".split(): + sys.path.append(os.path.join(lib_dir, name)) + # It is possible people with old versions installed with still have + # pywintypes and pythoncom registered. We no longer need this, and stale + # entries hurt us. + for name in "pythoncom pywintypes".split(): + keyname = "Software\\Python\\PythonCore\\" + sys.winver + "\\Modules\\" + name + for root in winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER: + try: + winreg.DeleteKey(root, keyname + "\\Debug") + except WindowsError: + pass + try: + winreg.DeleteKey(root, keyname) + except WindowsError: + pass + LoadSystemModule(lib_dir, "pywintypes") + LoadSystemModule(lib_dir, "pythoncom") + import win32api + + # and now we can get the system directory: + files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) + if not files: + raise RuntimeError("No system files to copy!!") + # Try the system32 directory first - if that fails due to "access denied", + # it implies a non-admin user, and we use sys.prefix + for dest_dir in [get_system_dir(), sys.prefix]: + # and copy some files over there + worked = 0 + try: + for fname in files: + base = os.path.basename(fname) + dst = os.path.join(dest_dir, base) + CopyTo("installing %s" % base, fname, dst) + if verbose: + print("Copied %s to %s" % (base, dst)) + # Register the files with the uninstaller + file_created(dst) + worked = 1 + # Nuke any other versions that may exist - having + # duplicates causes major headaches. + bad_dest_dirs = [ + os.path.join(sys.prefix, "Library\\bin"), + os.path.join(sys.prefix, "Lib\\site-packages\\win32"), + ] + if dest_dir != sys.prefix: + bad_dest_dirs.append(sys.prefix) + for bad_dest_dir in bad_dest_dirs: + bad_fname = os.path.join(bad_dest_dir, base) + if os.path.exists(bad_fname): + # let exceptions go here - delete must succeed + os.unlink(bad_fname) + if worked: + break + except win32api.error as details: + if details.winerror == 5: + # access denied - user not admin - try sys.prefix dir, + # but first check that a version doesn't already exist + # in that place - otherwise that one will still get used! + if os.path.exists(dst): + msg = ( + "The file '%s' exists, but can not be replaced " + "due to insufficient permissions. You must " + "reinstall this software as an Administrator" % dst + ) + print(msg) + raise RuntimeError(msg) + continue + raise + else: + raise RuntimeError( + "You don't have enough permissions to install the system files" + ) + + # Pythonwin 'compiles' config files - record them for uninstall. + pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") + for fname in glob.glob(os.path.join(pywin_dir, "*.cfg")): + file_created(fname[:-1] + "c") # .cfg->.cfc + + # Register our demo COM objects. + try: + try: + RegisterCOMObjects() + except win32api.error as details: + if details.winerror != 5: # ERROR_ACCESS_DENIED + raise + print("You do not have the permissions to install COM objects.") + print("The sample COM objects were not registered.") + except Exception: + print("FAILED to register the Python COM objects") + traceback.print_exc() + + # There may be no main Python key in HKCU if, eg, an admin installed + # python itself. + winreg.CreateKey(get_root_hkey(), root_key_name) + + chm_file = None + try: + chm_file = RegisterHelpFile(True, lib_dir) + except Exception: + print("Failed to register help file") + traceback.print_exc() + else: + if verbose: + print("Registered help file") + + # misc other fixups. + fixup_dbi() + + # Register Pythonwin in context menu + try: + RegisterPythonwin(True, lib_dir) + except Exception: + print("Failed to register pythonwin as editor") + traceback.print_exc() + else: + if verbose: + print("Pythonwin has been registered in context menu") + + # Create the win32com\gen_py directory. + make_dir = os.path.join(lib_dir, "win32com", "gen_py") + if not os.path.isdir(make_dir): + if verbose: + print("Creating directory %s" % (make_dir,)) + directory_created(make_dir) + os.mkdir(make_dir) + + try: + # create shortcuts + # CSIDL_COMMON_PROGRAMS only available works on NT/2000/XP, and + # will fail there if the user has no admin rights. + fldr = get_shortcuts_folder() + # If the group doesn't exist, then we don't make shortcuts - its + # possible that this isn't a "normal" install. + if os.path.isdir(fldr): + dst = os.path.join(fldr, "PythonWin.lnk") + create_shortcut( + os.path.join(lib_dir, "Pythonwin\\Pythonwin.exe"), + "The Pythonwin IDE", + dst, + "", + sys.prefix, + ) + file_created(dst) + if verbose: + print("Shortcut for Pythonwin created") + # And the docs. + if chm_file: + dst = os.path.join(fldr, "Python for Windows Documentation.lnk") + doc = "Documentation for the PyWin32 extensions" + create_shortcut(chm_file, doc, dst) + file_created(dst) + if verbose: + print("Shortcut to documentation created") + else: + if verbose: + print("Can't install shortcuts - %r is not a folder" % (fldr,)) + except Exception as details: + print(details) + + # importing win32com.client ensures the gen_py dir created - not strictly + # necessary to do now, but this makes the installation "complete" + try: + import win32com.client # noqa + except ImportError: + # Don't let this error sound fatal + pass + print("The pywin32 extensions were successfully installed.") + + if is_bdist_wininst: + # Open a web page with info about the .exe installers being deprecated. + import webbrowser + + try: + webbrowser.open("https://mhammond.github.io/pywin32_installers.html") + except webbrowser.Error: + print("Please visit https://mhammond.github.io/pywin32_installers.html") + + +def uninstall(lib_dir): + # First ensure our system modules are loaded from pywin32_system, so + # we can remove the ones we copied... + LoadSystemModule(lib_dir, "pywintypes") + LoadSystemModule(lib_dir, "pythoncom") + + try: + RegisterCOMObjects(False) + except Exception as why: + print("Failed to unregister COM objects: %s" % (why,)) + + try: + RegisterHelpFile(False, lib_dir) + except Exception as why: + print("Failed to unregister help file: %s" % (why,)) + else: + if verbose: + print("Unregistered help file") + + try: + RegisterPythonwin(False, lib_dir) + except Exception as why: + print("Failed to unregister Pythonwin: %s" % (why,)) + else: + if verbose: + print("Unregistered Pythonwin") + + try: + # remove gen_py directory. + gen_dir = os.path.join(lib_dir, "win32com", "gen_py") + if os.path.isdir(gen_dir): + shutil.rmtree(gen_dir) + if verbose: + print("Removed directory %s" % (gen_dir,)) + + # Remove pythonwin compiled "config" files. + pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") + for fname in glob.glob(os.path.join(pywin_dir, "*.cfc")): + os.remove(fname) + + # The dbi.pyd.old files we may have created. + try: + os.remove(os.path.join(lib_dir, "win32", "dbi.pyd.old")) + except os.error: + pass + try: + os.remove(os.path.join(lib_dir, "win32", "dbi_d.pyd.old")) + except os.error: + pass + + except Exception as why: + print("Failed to remove misc files: %s" % (why,)) + + try: + fldr = get_shortcuts_folder() + for link in ("PythonWin.lnk", "Python for Windows Documentation.lnk"): + fqlink = os.path.join(fldr, link) + if os.path.isfile(fqlink): + os.remove(fqlink) + if verbose: + print("Removed %s" % (link,)) + except Exception as why: + print("Failed to remove shortcuts: %s" % (why,)) + # Now remove the system32 files. + files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) + # Try the system32 directory first - if that fails due to "access denied", + # it implies a non-admin user, and we use sys.prefix + try: + for dest_dir in [get_system_dir(), sys.prefix]: + # and copy some files over there + worked = 0 + for fname in files: + base = os.path.basename(fname) + dst = os.path.join(dest_dir, base) + if os.path.isfile(dst): + try: + os.remove(dst) + worked = 1 + if verbose: + print("Removed file %s" % (dst)) + except Exception: + print("FAILED to remove %s" % (dst,)) + if worked: + break + except Exception as why: + print("FAILED to remove system files: %s" % (why,)) + + +# NOTE: If this script is run from inside the bdist_wininst created +# binary installer or uninstaller, the command line args are either +# '-install' or '-remove'. + +# Important: From inside the binary installer this script MUST NOT +# call sys.exit() or raise SystemExit, otherwise not only this script +# but also the installer will terminate! (Is there a way to prevent +# this from the bdist_wininst C code?) + + +def verify_destination(location): + if not os.path.isdir(location): + raise argparse.ArgumentTypeError('Path "{}" does not exist!'.format(location)) + return location + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="""A post-install script for the pywin32 extensions. + + * Typical usage: + + > python pywin32_postinstall.py -install + + If you installed pywin32 via a .exe installer, this should be run + automatically after installation, but if it fails you can run it again. + + If you installed pywin32 via PIP, you almost certainly need to run this to + setup the environment correctly. + + Execute with script with a '-install' parameter, to ensure the environment + is setup correctly. + """, + ) + parser.add_argument( + "-install", + default=False, + action="store_true", + help="Configure the Python environment correctly for pywin32.", + ) + parser.add_argument( + "-remove", + default=False, + action="store_true", + help="Try and remove everything that was installed or copied.", + ) + parser.add_argument( + "-wait", + type=int, + help="Wait for the specified process to terminate before starting.", + ) + parser.add_argument( + "-silent", + default=False, + action="store_true", + help='Don\'t display the "Abort/Retry/Ignore" dialog for files in use.', + ) + parser.add_argument( + "-quiet", + default=False, + action="store_true", + help="Don't display progress messages.", + ) + parser.add_argument( + "-destination", + default=sysconfig.get_paths()["platlib"], + type=verify_destination, + help="Location of the PyWin32 installation", + ) + + args = parser.parse_args() + + if not args.quiet: + print("Parsed arguments are: {}".format(args)) + + if not args.install ^ args.remove: + parser.error("You need to either choose to -install or -remove!") + + if args.wait is not None: + try: + os.waitpid(args.wait, 0) + except os.error: + # child already dead + pass + + silent = args.silent + verbose = not args.quiet + + if args.install: + install(args.destination) + + if args.remove: + if not is_bdist_wininst: + uninstall(args.destination) + + +if __name__ == "__main__": + main() diff --git a/lab_4/aimenv/Scripts/pywin32_testall.py b/lab_4/aimenv/Scripts/pywin32_testall.py new file mode 100644 index 0000000..a54f9d4 --- /dev/null +++ b/lab_4/aimenv/Scripts/pywin32_testall.py @@ -0,0 +1,124 @@ +"""A test runner for pywin32""" +import os +import site +import subprocess +import sys + +# locate the dirs based on where this script is - it may be either in the +# source tree, or in an installed Python 'Scripts' tree. +this_dir = os.path.dirname(__file__) +site_packages = [ + site.getusersitepackages(), +] + site.getsitepackages() + +failures = [] + + +# Run a test using subprocess and wait for the result. +# If we get an returncode != 0, we know that there was an error, but we don't +# abort immediately - we run as many tests as we can. +def run_test(script, cmdline_extras): + dirname, scriptname = os.path.split(script) + # some tests prefer to be run from their directory. + cmd = [sys.executable, "-u", scriptname] + cmdline_extras + print("--- Running '%s' ---" % script) + sys.stdout.flush() + result = subprocess.run(cmd, check=False, cwd=dirname) + print("*** Test script '%s' exited with %s" % (script, result.returncode)) + sys.stdout.flush() + if result.returncode: + failures.append(script) + + +def find_and_run(possible_locations, extras): + for maybe in possible_locations: + if os.path.isfile(maybe): + run_test(maybe, extras) + break + else: + raise RuntimeError( + "Failed to locate a test script in one of %s" % possible_locations + ) + + +def main(): + import argparse + + code_directories = [this_dir] + site_packages + + parser = argparse.ArgumentParser( + description="A script to trigger tests in all subprojects of PyWin32." + ) + parser.add_argument( + "-no-user-interaction", + default=False, + action="store_true", + help="(This is now the default - use `-user-interaction` to include them)", + ) + + parser.add_argument( + "-user-interaction", + action="store_true", + help="Include tests which require user interaction", + ) + + parser.add_argument( + "-skip-adodbapi", + default=False, + action="store_true", + help="Skip the adodbapi tests; useful for CI where there's no provider", + ) + + args, remains = parser.parse_known_args() + + # win32, win32ui / Pythonwin + + extras = [] + if args.user_interaction: + extras += ["-user-interaction"] + extras.extend(remains) + scripts = [ + "win32/test/testall.py", + "Pythonwin/pywin/test/all.py", + ] + for script in scripts: + maybes = [os.path.join(directory, script) for directory in code_directories] + find_and_run(maybes, extras) + + # win32com + maybes = [ + os.path.join(directory, "win32com", "test", "testall.py") + for directory in [ + os.path.join(this_dir, "com"), + ] + + site_packages + ] + extras = remains + ["1"] # only run "level 1" tests in CI + find_and_run(maybes, extras) + + # adodbapi + if not args.skip_adodbapi: + maybes = [ + os.path.join(directory, "adodbapi", "test", "adodbapitest.py") + for directory in code_directories + ] + find_and_run(maybes, remains) + # This script has a hard-coded sql server name in it, (and markh typically + # doesn't have a different server to test on) but there is now supposed to be a server out there on the Internet + # just to run these tests, so try it... + maybes = [ + os.path.join(directory, "adodbapi", "test", "test_adodbapi_dbapi20.py") + for directory in code_directories + ] + find_and_run(maybes, remains) + + if failures: + print("The following scripts failed") + for failure in failures: + print(">", failure) + sys.exit(1) + print("All tests passed \\o/") + + +if __name__ == "__main__": + main() diff --git a/lab_4/aimenv/pyvenv.cfg b/lab_4/aimenv/pyvenv.cfg new file mode 100644 index 0000000..b18a31c --- /dev/null +++ b/lab_4/aimenv/pyvenv.cfg @@ -0,0 +1,5 @@ +home = C:\Users\Egor\AppData\Local\Programs\Python\Python312 +include-system-site-packages = false +version = 3.12.5 +executable = C:\Users\Egor\AppData\Local\Programs\Python\Python312\python.exe +command = C:\Users\Egor\AppData\Local\Programs\Python\Python312\python.exe -m venv C:\Users\Egor\Desktop\ULSTU\AI\aim\aimenv diff --git a/lab_4/aimenv/share/jupyter/kernels/python3/kernel.json b/lab_4/aimenv/share/jupyter/kernels/python3/kernel.json new file mode 100644 index 0000000..cca38a4 --- /dev/null +++ b/lab_4/aimenv/share/jupyter/kernels/python3/kernel.json @@ -0,0 +1,14 @@ +{ + "argv": [ + "python", + "-m", + "ipykernel_launcher", + "-f", + "{connection_file}" + ], + "display_name": "Python 3 (ipykernel)", + "language": "python", + "metadata": { + "debugger": true + } +} \ No newline at end of file diff --git a/lab_4/aimenv/share/jupyter/kernels/python3/logo-32x32.png b/lab_4/aimenv/share/jupyter/kernels/python3/logo-32x32.png new file mode 100644 index 0000000..be81330 Binary files /dev/null and b/lab_4/aimenv/share/jupyter/kernels/python3/logo-32x32.png differ diff --git a/lab_4/aimenv/share/jupyter/kernels/python3/logo-64x64.png b/lab_4/aimenv/share/jupyter/kernels/python3/logo-64x64.png new file mode 100644 index 0000000..eebbff6 Binary files /dev/null and b/lab_4/aimenv/share/jupyter/kernels/python3/logo-64x64.png differ diff --git a/lab_4/aimenv/share/jupyter/kernels/python3/logo-svg.svg b/lab_4/aimenv/share/jupyter/kernels/python3/logo-svg.svg new file mode 100644 index 0000000..467b07b --- /dev/null +++ b/lab_4/aimenv/share/jupyter/kernels/python3/logo-svg.svg @@ -0,0 +1,265 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab_4/aimenv/share/man/man1/ipython.1 b/lab_4/aimenv/share/man/man1/ipython.1 new file mode 100644 index 0000000..0f4a191 --- /dev/null +++ b/lab_4/aimenv/share/man/man1/ipython.1 @@ -0,0 +1,60 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH IPYTHON 1 "July 15, 2011" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) and groff_man(7) +.\" .SH section heading +.\" .SS secondary section heading +.\" +.\" +.\" To preview this page as plain text: nroff -man ipython.1 +.\" +.SH NAME +ipython \- Tools for Interactive Computing in Python. +.SH SYNOPSIS +.B ipython +.RI [ options ] " files" ... + +.B ipython subcommand +.RI [ options ] ... + +.SH DESCRIPTION +An interactive Python shell with automatic history (input and output), dynamic +object introspection, easier configuration, command completion, access to the +system shell, integration with numerical and scientific computing tools, +web notebook, Qt console, and more. + +For more information on how to use IPython, see 'ipython \-\-help', +or 'ipython \-\-help\-all' for all available command\(hyline options. + +.SH "ENVIRONMENT VARIABLES" +.sp +.PP +\fIIPYTHONDIR\fR +.RS 4 +This is the location where IPython stores all its configuration files. The default +is $HOME/.ipython if IPYTHONDIR is not defined. + +You can see the computed value of IPYTHONDIR with `ipython locate`. + +.SH FILES + +IPython uses various configuration files stored in profiles within IPYTHONDIR. +To generate the default configuration files and start configuring IPython, +do 'ipython profile create', and edit '*_config.py' files located in +IPYTHONDIR/profile_default. + +.SH AUTHORS +IPython is written by the IPython Development Team . diff --git a/lab_4/lab3.ipynb b/lab_4/lab3.ipynb new file mode 100644 index 0000000..7c9e25e --- /dev/null +++ b/lab_4/lab3.ipynb @@ -0,0 +1,545 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Вариант задания: Прогнозирование цен на автомобили\n", + "### Бизнес-цели:\n", + "Повышение эффективности ценообразования на вторичном рынке автомобилей:\n", + "Цель: Разработать модель машинного обучения, которая позволит точно прогнозировать рыночную стоимость автомобилей на вторичном рынке.\n", + "Ключевые показатели успеха (KPI):\n", + "Точность прогнозирования цены (например, RMSE, MAE).\n", + "Сокращение времени на оценку стоимости автомобиля.\n", + "Увеличение количества продаж за счет более конкурентоспособных цен.\n", + "Оптимизация рекламных бюджетов для онлайн-площадок по продаже автомобилей:\n", + "Цель: Использовать прогнозы цен на автомобили для оптимизации таргетинга рекламы и повышения конверсии на онлайн-площадках.\n", + "Ключевые показатели успеха (KPI):\n", + "Увеличение CTR (Click-Through Rate) рекламных объявлений.\n", + "Повышение конверсии (процент пользователей, совершивших покупку после клика на рекламу).\n", + "Снижение стоимости привлечения клиента (CPA).\n", + "### Цели технического проекта:\n", + "Для бизнес-цели 1:\n", + "Сбор и подготовка данных:\n", + "Очистка данных от пропусков, выбросов и дубликатов.\n", + "Преобразование категориальных переменных в числовые.\n", + "Разделение данных на обучающую и тестовую выборки.\n", + "Разработка и обучение модели:\n", + "Исследование различных алгоритмов машинного обучения (линейная регрессия, деревья решений, случайный лес и т.д.).\n", + "Обучение моделей на обучающей выборке.\n", + "Оценка качества моделей на тестовой выборке с помощью метрик RMSE, MAE и др.\n", + "Развертывание модели:\n", + "Интеграция модели в существующую систему или разработка нового API для доступа к прогнозам.\n", + "Создание веб-интерфейса или мобильного приложения для удобного использования модели.\n", + "Для бизнес-цели 2:\n", + "Анализ данных о пользователях и поведении:\n", + "Анализ данных о просмотрах, кликах и покупках на онлайн-площадке.\n", + "Определение сегментов пользователей с разным уровнем интереса к покупке автомобилей.\n", + "Разработка рекомендательной системы:\n", + "Создание модели, которая будет рекомендовать пользователям автомобили, соответствующие их предпочтениям и бюджету.\n", + "Интеграция рекомендательной системы в рекламные кампании.\n", + "Оптимизация таргетинга рекламы:\n", + "Использование прогнозов цен на автомобили для более точного таргетинга рекламы на пользователей, готовых к покупке.\n", + "Тестирование различных стратегий таргетинга и оценка их эффективности." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['ID', 'Price', 'Levy', 'Manufacturer', 'Model', 'Prod. year',\n", + " 'Category', 'Leather interior', 'Fuel type', 'Engine volume', 'Mileage',\n", + " 'Cylinders', 'Gear box type', 'Drive wheels', 'Doors', 'Wheel', 'Color',\n", + " 'Airbags'],\n", + " dtype='object')\n" + ] + } + ], + "source": [ + "import pandas as pn\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "import matplotlib.ticker as ticker\n", + "df = pn.read_csv(\".//static//csv//car_price_prediction.csv\")\n", + "print(df.columns)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Разделим на 3 выборки\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Размер обучающей выборки: 12311\n", + "Размер контрольной выборки: 3078\n", + "Размер тестовой выборки: 3848\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "# Разделение данных на обучающую и тестовую выборки (80% - обучение, 20% - тест)\n", + "train_data, test_data = train_test_split(df, test_size=0.2, random_state=42)\n", + "\n", + "# Разделение обучающей выборки на обучающую и контрольную (80% - обучение, 20% - контроль)\n", + "train_data, val_data = train_test_split(train_data, test_size=0.2, random_state=42)\n", + "\n", + "print(\"Размер обучающей выборки:\", len(train_data))\n", + "print(\"Размер контрольной выборки:\", len(val_data))\n", + "print(\"Размер тестовой выборки:\", len(test_data))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHHCAYAAABeLEexAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABI9UlEQVR4nO3deVxVdf7H8fdlVxRckK1IERVzNysHHbdCFJdyssylxMbSccAWW4yZTLQF08bMdHSaUmu0sWyxpmks3LKS1FTcM3Uw0wR3rmKiwPf3hz/ueL2gQsIFz+v5eJzHg3PO95zzOd+7vTnLvTZjjBEAAICFebi7AAAAAHcjEAEAAMsjEAEAAMsjEAEAAMsjEAEAAMsjEAEAAMsjEAEAAMsjEAEAAMsjEAGAm5w4cUK7d+9Wfn6+u0vBVWSM0bFjx7Rr1y53l4JSIBABQAU5d+6cJk+erNatW8vX11e1a9dW48aNtWzZMneXViVs3bpVixcvdoxnZGTo3//+t/sKusDJkyf1zDPPKDo6Wj4+Pqpbt66aNGminTt3urs0XCEvdxeAymHevHl64IEHHOO+vr664YYbFBcXp3HjxikkJMSN1QFVX15enuLi4vTtt9/qD3/4g5577jlVr15dnp6eateunbvLqxJOnjypkSNHKjQ0VHXr1tUjjzyi+Ph49e7d2611HT16VF26dNG+ffs0evRodezYUT4+PvL29laDBg3cWhuuHIEITiZOnKjIyEidOXNGX3/9tWbNmqXPPvtMW7duVfXq1d1dHlBlvfTSS1qzZo0+//xzde3a1d3lVEkxMTGOQZKaNGmihx56yM1VSU8++aQOHjyo9PR0NW/e3N3loIwIRHASHx+vm2++WZL04IMPqm7dupo6dao+/vhjDRo0yM3VAVVTfn6+pk2bpscff5ww9CstXrxY27dv1y+//KKWLVvKx8fHrfUcOnRIb731lmbPnk0YquK4hgiXdNttt0mSMjMzJUnHjh3TE088oZYtW6pGjRoKCAhQfHy8Nm3a5LLsmTNnlJKSoiZNmsjPz09hYWG66667tGfPHknS3r17ZbPZShwu/OBYuXKlbDab3n33Xf3pT39SaGio/P39dccdd+inn35y2faaNWvUs2dPBQYGqnr16urSpYu++eabYvexa9euxW4/JSXFpe38+fPVrl07VatWTXXq1NHAgQOL3f6l9u1ChYWFmjZtmpo3by4/Pz+FhIRo5MiROn78uFO7Bg0aqE+fPi7bSUpKcllncbVPmTLFpU+l86dxxo8fr0aNGsnX11cRERF66qmnlJeXV2xfXahr165q0aKFy/SXX35ZNptNe/fudZp+4sQJPfroo4qIiJCvr68aNWqkl156SYWFhY42Rf328ssvu6y3RYsWxT4n3n///RJrHDZs2BWdsmjQoIHj8fHw8FBoaKjuvfde7du377LLStJf//pXNW/eXL6+vgoPD1diYqJOnDjhmL9z504dP35cNWvWVJcuXVS9enUFBgaqT58+2rp1q6PdihUrZLPZ9NFHH7ls45133pHNZlN6erqj5mHDhjm1KeqTlStXOqZ99dVXuueee3TDDTc4HuPHHntMv/zyi9OyKSkpLs+lBQsWqE2bNvLz81PdunU1aNAglz4ZNmyYatSo4TTt/fffd6lDkmrUqOFSs3Rlr6uuXbs6Hv9mzZqpXbt22rRpU7Gvq+Jc/DoPCgpS7969nfpfOv/6SUpKKnE98+bNc3p+r1u3ToWFhTp79qxuvvnmS/aVJC1fvlydOnWSv7+/atWqpTvvvFM7duxwalP0WHz//fcaMGCAAgICHKcIz5w541Lvha/3/Px89erVS3Xq1NH27dud2l7p+5dVcYQIl1QUXurWrStJ+u9//6vFixfrnnvuUWRkpLKzs/W3v/1NXbp00fbt2xUeHi5JKigoUJ8+fbRs2TINHDhQjzzyiE6ePKm0tDRt3bpVUVFRjm0MGjRIvXr1ctpucnJysfW88MILstlsGjt2rA4dOqRp06YpNjZWGRkZqlatmqTzbzjx8fFq166dxo8fLw8PD82dO1e33XabvvrqK916660u673++uuVmpoqSTp16pRGjRpV7LbHjRunAQMG6MEHH9Thw4f12muvqXPnztq4caNq1arlssyIESPUqVMnSdKHH37o8kE3cuRIx/VbDz/8sDIzMzVjxgxt3LhR33zzjby9vYvth9I4ceKEY98uVFhYqDvuuENff/21RowYoRtvvFFbtmzRK6+8oh9++MHp4tVf6/Tp0+rSpYsOHDigkSNH6oYbbtDq1auVnJysgwcPatq0aVdtW2XVqVMnjRgxQoWFhdq6daumTZumn3/+WV999dUll0tJSdGECRMUGxurUaNGaefOnZo1a5bWrVvneAyPHj0q6fzzunHjxpowYYLOnDmjmTNnqmPHjlq3bp2aNGmirl27KiIiQgsWLNDvfvc7p+0sWLBAUVFRjtNFV2rRokU6ffq0Ro0apbp162rt2rV67bXXtH//fi1atKjE5d555x3dd999at26tVJTU3X06FFNnz5dX3/9tTZu3KigoKBS1VGSsryuiowdO7ZU22ratKn+/Oc/yxijPXv2aOrUqerVq9cVB9/iFD22SUlJateunSZNmqTDhw8X21dLly5VfHy8GjZsqJSUFP3yyy967bXX1LFjR23YsMElvA8YMEANGjRQamqqvv32W02fPl3Hjx/X22+/XWI9Dz74oFauXKm0tDQ1a9bMMf3X9LNlGMAYM3fuXCPJLF261Bw+fNj89NNPZuHChaZu3bqmWrVqZv/+/cYYY86cOWMKCgqcls3MzDS+vr5m4sSJjmlz5swxkszUqVNdtlVYWOhYTpKZMmWKS5vmzZubLl26OMZXrFhhJJnrrrvO2O12x/T33nvPSDKvvvqqY92NGzc2PXr0cGzHGGNOnz5tIiMjTffu3V221aFDB9OiRQvH+OHDh40kM378eMe0vXv3Gk9PT/PCCy84Lbtlyxbj5eXlMn3Xrl1Gknnrrbcc08aPH28ufMl99dVXRpJZsGCB07JLlixxmV6/fn3Tu3dvl9oTExPNxS/ji2t/6qmnTHBwsGnXrp1Tn/7jH/8wHh4e5quvvnJafvbs2UaS+eabb1y2d6EuXbqY5s2bu0yfMmWKkWQyMzMd05577jnj7+9vfvjhB6e2Tz/9tPH09DT79u0zxpTtObFo0aISa0xISDD169e/5H4Yc75/ExISnKYNHjzYVK9e/ZLLHTp0yPj4+Ji4uDin18WMGTOMJDNnzhynWoOCgsyRI0cc7X744Qfj7e1t+vfv75iWnJxsfH19zYkTJ5y24+Xl5fS4RkZGmqFDhzrVU7SdFStWOKadPn3ape7U1FRjs9nMjz/+6Jh24fMzPz/fhISEmKioKHPq1ClHm5UrVxpJ5vHHH3dMS0hIMP7+/k7rX7RokUsdxhjj7+/v1M+leV116dLF6fH/7LPPjCTTs2dPl9dAcS5e3hhj/vSnPxlJ5tChQ45pkkxiYmKJ6yl6ryx6fheNN2vWzKmvix6LC/uqTZs2Jjg42Bw9etQxbdOmTcbDw8PpsSx6LO644w6nbf/xj380ksymTZuc6i16XiQnJxtPT0+zePFip+VK+/5lVZwyg5PY2FjVq1dPERERGjhwoGrUqKGPPvpI1113naTzd595eJx/2hQUFOjo0aOqUaOGoqOjtWHDBsd6PvjgAwUFBWn06NEu27iSw9slGTp0qGrWrOkYv/vuuxUWFqbPPvtM0vnbcHft2qXBgwfr6NGjOnLkiI4cOaLc3FzdfvvtWrVqldMpGun8qT0/P79LbvfDDz9UYWGhBgwY4FjnkSNHFBoaqsaNG2vFihVO7c+ePSvpfH+VZNGiRQoMDFT37t2d1tmuXTvVqFHDZZ3nzp1zanfkyBGXw+cXO3DggF577TWNGzfO5bTGokWLdOONN6pp06ZO6yw6TXrx9n+NRYsWqVOnTqpdu7bTtmJjY1VQUKBVq1Y5tT99+rTLvhYUFBS77pMnT+rIkSNOp6jKIi8vT0eOHNGhQ4eUlpam5cuX6/bbb7/kMkuXLtXZs2f16KOPOl4XkvTQQw8pICDA5ZbwBx54wHG0VZIaN26sO+64Q0uWLHHs39ChQ5WXl+d0KvDdd99Vfn6+7rvvPse04OBg7d+//7L7VXTkVJJyc3N15MgRdejQQcYYbdy40aX9kSNHtHLlSmVnZ2vkyJHy9/d3zOvSpYvatWt31W51L+3rqogxRsnJyerfv7/at29/xdsreg0dPnxY6enp+uijj9SqVSuXo11nzpzRkSNHdPToUZf3i5IkJiY69XXXrl2d+urgwYPKyMjQsGHDVKdOHUe7Vq1aqXv37o73sIvXeaGi99Pi2s6YMUOpqamaPn267rzzTqd5Ze1nq+GUGZzMnDlTTZo0kZeXl0JCQhQdHe30Rl9YWKhXX31Vf/3rX5WZmen0IXXhG/2ePXsUHR0tL6+r+xRr3Lix07jNZlOjRo0c5/OLvggtISGhxHXk5OSodu3ajvEjR464rPdiu3btkjGmxHYXn9oq+nC+OIRcvM6cnBwFBwcXO//QoUNO41988YXq1at3yTovNn78eIWHh2vkyJEu19rs2rVLO3bsKHGdF2//19i1a5c2b958xdsaP368xo8f79KuuK9/+P3vf+/4u0aNGurbt69eeeWVUn9VxMKFC7Vw4ULH+C233KI33njjksv8+OOPkqTo6Gin6T4+PmrYsKFjftE/AU2bNnVZx4033qgPPvhAR44cUUhIiJo2bapbbrlFCxYs0PDhwyWdP132m9/8Ro0aNXIs16FDB02fPl0LFy7UbbfdJg8PD+Xk5Lisf9++fXr22Wf1ySefuFybVlz7Cx+ji/erqN5LXbdVGqV9XRVZsGCBtm3bpvfee0/vvPPOFW9v9erVTvvXuHFjLV682OWftDfffFNvvvmmpPOPZfv27TV16lTHDScXutxjW9RXJT1Xitp9/vnnys3NdQqgF/dLVFSUPDw8XK7P+89//qPvvvtO0vnrPC9W1n62GgIRnNx6663FvuiLvPjiixo3bpx+//vf67nnnlOdOnXk4eGhRx999Ir/kypPRTVMmTJFbdq0KbbNhSHl7NmzOnjwoLp3737Z9dpsNv3nP/+Rp6fnJdcpSVlZWZKk0NDQS64zODhYCxYsKHb+xeGhffv2ev75552mzZgxQx9//HGxy+/YsUPz5s3T/Pnzi33DKywsVMuWLTV16tRil4+IiCix9tIqLCxU9+7d9dRTTxU7v0mTJk7jI0aM0D333OM0raTbq5999ll16tRJ586d0/r16zVx4kSdOHGi2P+iLyUuLk5PPvmkJGn//v166aWX1K1bN3333XdO//mXRWmXHzp0qB555BHt379feXl5+vbbbzVjxgynNn/605/0zTffXPLuz4KCAnXv3l3Hjh3T2LFj1bRpU/n7++vAgQMaNmxYsa/ZtLQ0paen69lnny1VzWVR2teVdP41O27cOA0fPtzleXM5rVq10l/+8hdJclzn07VrV23YsMHptXrnnXcqKSlJxhhlZmZq4sSJ6tOnT7HfPP1rnxulUdLR9bVr1+qhhx6Sv7+/nn/+ed1zzz1Owass/WxFBCKUyvvvv69u3bo5/nsqcuLECafDzlFRUVqzZo3OnTt3Vf/7uPgNyRij3bt3q1WrVo7tSlJAQIBiY2Mvu75Nmzbp3LlzlwyBRes1xigyMvKK3oS3b98um81W7H+DF65z6dKl6tix4xW9qQYFBbns06UufE5OTlabNm107733lrj9TZs26fbbb/9VpzGvRFRUlE6dOnVFj4l0/j/ji9te+J/zhVq2bOloGx8fr3379umtt94q9c9hhIWFOW0zOjpaHTp00OLFi0sMHfXr15d0/i6yhg0bOqafPXtWmZmZjvVFRkY62l3s+++/l7+/v9PrZ+DAgRozZoz++c9/6pdffpG3t7fL4xgUFKT09HRt377dEcA3bdqkJ554wtFmy5Yt+uGHH/TWW29p6NChjulpaWkl9kNsbKwCAwP17LPPlljv1fqywdK+rqTzd/QdOnSo2LtAL6d27dpOj3HXrl0VHh6uuXPnOt3Icf311zu1q1GjhoYMGVLsKcYLH9ui081FLuyrC58rF/v+++8VFBTk8hzftWuXY/2StHv3bhUWFrr0f/fu3TVr1iydOXNGixcv1ogRIxx3HEpl62cr4hoilIqnp6eMMU7TFi1apAMHDjhN69+/v44cOeLyX60kl+VL4+2339bJkycd4++//74OHjyo+Ph4SVK7du0UFRWll19+WadOnXJZ/vDhwy61e3p6FntL+4XuuusueXp6asKECS71G2Mcd5pI5297/eCDD3Trrbde8j+vAQMGqKCgQM8995zLvPz8/F91TUx6ero+/vhjTZo0qcSwM2DAAB04cEB///vfXeb98ssvys3NLfP2i9tWenq6Pv/8c5d5J06cuKq/5VVYWCgPD49fHfKKbku/1FcQxMbGysfHR9OnT3d6Xrz55pvKyclxfINyvXr1dPPNN+utt95yOm21Z88effLJJ4qPj3f6zz0oKEjx8fGaP3++FixYoJ49exZ7V5eHh4datGih2NhYxcbGunzjddE6L6zNGKNXX331kvvepk0bhYSE6O9//7tOnz7tmP7VV1/pu+++u+zr5UqV5nUlnb9e7IUXXtBjjz12yaOvV+pKHmPpf0eeizu60rZtW4WGhmr27NlO67m4r8LCwtSmTRu99dZbTq/trVu36osvvnC501Y6fwnDhV577TVJcrzfFenQoYM8PT3l7++v2bNna9WqVU6v69L2s1VxhAil0qdPH02cOFEPPPCAOnTooC1btmjBggVO/x1L5w/5v/322xozZozWrl2rTp06KTc3V0uXLtUf//hHl4v+rlSdOnX029/+Vg888ICys7M1bdo0NWrUyHE6xcPDQ2+88Ybi4+PVvHlzPfDAA7ruuut04MABrVixQgEBAfrXv/6l3NxczZw5U9OnT1eTJk2cvi+lKEht3rxZ6enpiomJUVRUlJ5//nklJydr79696tevn2rWrKnMzEx99NFHGjFihJ544gktXbpU48aN0+bNm/Wvf/3rkvvSpUsXjRw5UqmpqcrIyFBcXJy8vb21a9cuLVq0SK+++qruvvvuMvXTF198oe7du1/yiMz999+v9957T3/4wx+0YsUKdezYUQUFBfr+++/13nvv6fPPP7/skbNTp05pyZIlTtOK/gP+8ssv5e3treuuu05PPvmkPvnkE/Xp00fDhg1Tu3btlJubqy1btuj999/X3r17y3wbd0ZGhmrUqKH8/HytX79eb7/9tu68885iP7wu5b///a/mz58v6fzF6DNmzFBAQMAlL6yuV6+ekpOTNWHCBPXs2VN33HGHdu7cqb/+9a+65ZZbnC6Cnjx5suLi4hQTE6MHH3zQcdu9n5+fXnjhBZd1Dx061PH4Fxear0TTpk0VFRWlJ554QgcOHFBAQIA++OADl2uJLubt7a2XXnpJw4YNU8eOHZWQkKBjx47p1Vdf1XXXXedyu3tBQYHT8yAjI0PS+VM5F174X1BQoAMHDmjt2rW69dZbr/h1VWTDhg0KCgoq8dTr5WRnZzse4yNHjuhvf/ubvLy8XALevn37tGTJEscpsxdeeEH169dX27ZtXY5Se3l5afLkyRo6dKg6deqkIUOGOE7HXX/99U59NWXKFMXHxysmJkbDhw933HYfGBhY7BGvzMxM3XHHHerZs6fS09M1f/58DR48WK1bty5xH3v06KH77rtPTz31lPr27auwsLBS97NlVeQtbai8im4dXbdu3SXbnTlzxjz++OMmLCzMVKtWzXTs2NGkp6cXe0vr6dOnzZ///GcTGRlpvL29TWhoqLn77rvNnj17jDFlu8X6n//8p0lOTjbBwcGmWrVqpnfv3k63DhfZuHGjueuuu0zdunWNr6+vqV+/vhkwYIBZtmyZ07YvN1x8K/YHH3xgfvvb3xp/f3/j7+9vmjZtahITE83OnTuNMcaMHj3adO7c2SxZssSlpotvuy/y+uuvm3bt2plq1aqZmjVrmpYtW5qnnnrK/Pzzz442pb3t3mazmfXr1ztNL+4xOnv2rHnppZdM8+bNja+vr6ldu7Zp166dmTBhgsnJyXHZ3sXru1z/zZ0719H+5MmTJjk52TRq1Mj4+PiYoKAg06FDB/Pyyy+bs2fPGmPK9pwoGry8vEz9+vXNww8/bI4fP26MKd1t9xeuKygoyMTFxZn09PTLLmvM+dvsmzZtary9vU1ISIgZNWqUo4YLLVu2zHTs2NFUq1bNBAQEmN69e5stW7YUu868vDxTu3ZtExgYaH755ZcrqqO42+63b99uYmNjTY0aNUxQUJB56KGHzKZNm1wen+KenwsXLjRt2rRxPDfuvfdes3fvXqc2CQkJV/RaunC4+Hl4udeVMf97vr3yyitOy5b0urrYxc/XWrVqmY4dO5rPPvvMqd2FbWw2mwkNDTV33XWX2bFjhzHG9bb7Iu+9955p27at8fX1NXXq1DGDBg0q9r1p6dKlTs+Bvn37mu3btxe7T9u3bzd33323qVmzpqldu7ZJSkpyeS7ooq/ZMMaYI0eOmHr16pnf/e53TtOvpJ+tzGbMrzh/AVSQlStXqlu3blq0aFGZj5pcaO/evYqMjFRmZmaJ10OkpKRo7969mjdv3q/enhU1aNBAKSkpxX4zMS4vPz9f4eHh6tu3r8s1e1XZvHnzNG/ePJdvscb/FH3Z5+HDh6/aF2Di8riGCAAqocWLF+vw4cNOF0MDKD9cQwRLKrpr5FIXPbdq1crxUyQovS5duji+0BNXbs2aNdq8ebOee+45tW3bVl26dHF3SVfVddddV+zP5wDuRiCCJQUFBTkurizJXXfdVUHVXJveeustd5dQJc2aNUvz589XmzZtrsnTtd27d7/s934B7sA1RAAAwPK4hggAAFgegQgAAFge1xBdgcLCQv3888+qWbNmuf/EAQAAuDqMMTp58qTCw8Odfqi8OASiK/Dzzz9f1R+6BAAAFeenn37S9ddff8k2BKIrULNmTUnnOzQgIMDN1QAAgCtht9sVERHh+By/FALRFSg6TRYQEEAgAgCgirmSy124qBoAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFgegQgAAFieWwPRqlWr1LdvX4WHh8tms2nx4sVO8202W7HDlClTHG0aNGjgMn/SpElO69m8ebM6deokPz8/RUREaPLkyRWxewAAoIpwayDKzc1V69atNXPmzGLnHzx40GmYM2eObDab+vfv79Ru4sSJTu1Gjx7tmGe32xUXF6f69etr/fr1mjJlilJSUvT666+X674BAICqw8ud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jR49WmPHjr3iOu12uwIDA5WTk3PZU3YAAKByKM3nt1sDUVVBIAIAoOopzed3pb6GCAAAoCIQiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOURiAAAgOWVKRA1bNhQR48edZl+4sQJNWzY8FcXBQAAUJHKFIj27t2rgoICl+l5eXk6cODAry4KAACgInmVpvEnn3zi+Pvzzz9XYGCgY7ygoEDLli1TgwYNrlpxAAAAFaFUgahfv36SJJvNpoSEBKd53t7eatCggf7yl79cteIAAAAqQqkCUWFhoSQpMjJS69atU1BQULkUBQAAUJFKFYiKZGZmXu06AAAA3KZMgUiSli1bpmXLlunQoUOOI0dF5syZ86sLAwAAqChlCkQTJkzQxIkTdfPNNyssLEw2m+1q1wUAAFBhyhSIZs+erXnz5un++++/2vUAAABUuDJ9D9HZs2fVoUOHX73xVatWqW/fvgoPD5fNZtPixYud5htj9OyzzyosLEzVqlVTbGysdu3a5dTm2LFjGjJkiAICAlSrVi0NHz5cp06dcmqzefNmderUSX5+foqIiNDkyZN/de0AAODaUaZA9OCDD+qdd9751RvPzc1V69atNXPmzGLnT548WdOnT9fs2bO1Zs0a+fv7q0ePHjpz5oyjzZAhQ7Rt2zalpaXp008/1apVqzRixAjHfLvdrri4ONWvX1/r16/XlClTlJKSotdff/1X1w8AAK4NNmOMKe1CjzzyiN5++221atVKrVq1kre3t9P8qVOnlr4Qm00fffSR47uOjDEKDw/X448/rieeeEKSlJOTo5CQEM2bN08DBw7Ujh071KxZM61bt04333yzJGnJkiXq1auX9u/fr/DwcM2aNUt//vOflZWVJR8fH0nS008/rcWLF+v777+/otrsdrsCAwOVk5OjgICAUu8bAACoeKX5/C7TEaLNmzerTZs28vDw0NatW7Vx40bHkJGRUZZVusjMzFRWVpZiY2Md0wIDA9W+fXulp6dLktLT01WrVi1HGJKk2NhYeXh4aM2a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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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OxEREXUuLdrjYzQaccMNN7RKAf369UNSUhL279+PWbNmYcqUKUhNTW2VZTcmNjYWxcXF0uPs2bNtuj4iIiLqGFoUfB599FF8/vnnrVKAVqtFYGAgwsLCsGTJEgwePBhvvfUWDAYDjEYjioqKrNrn5OTAYDAAAAwGQ72rvCzDljYN0el00pVklgcRERF1fS061FVZWYkPPvgA27ZtQ2hoKDQajdX0ZcuWtbggy6XxYWFh0Gg0iI+Px6RJkwAAaWlpyMzMRHh4OAAgPDwcr7zyCnJzc+Hp6QkA2Lp1K/R6PQYMGNDiGoiIiKhralHwSU5OxpAhQwAAKSkpVtMUCkWTlxMbG4uoqCj06tULpaWl+Pzzz7Fz507prtDTpk3DvHnz4ObmBr1ejzlz5iA8PBwjR44EAIwbNw4DBgzAQw89hNdffx3Z2dl44YUXEBMTA51O15JNIyIioi6sRcFnx44drbLy3NxcPPzww7hw4QKcnZ0RGhqKLVu24LbbbgMAvPnmm1AqlZg0aRKqqqoQGRmJVatWSfOrVCps3LgRs2bNQnh4OBwdHTFlyhQsXry4VeojIiKirqXF9/HpSngfHyIios6nJd/fLdrjc8stt1zxkNb27dtbslgiIiKiNtWi4GM5v8eiuroaSUlJSElJqffjpUREREQdRYuCz5tvvtng+IULF6KsrOyaCiIiIiJqKy26j09j/va3v13T73QRERERtaVWDT4JCQmws7NrzUUSERERtZoWHeqaOHGi1bAQAhcuXMDBgwfx4osvtkphRERERK2tRcHH2dnZalipVKJfv35YvHgxxo0b1yqFEREREbW2FgWf1atXt3YdRERERG2uRcHHIjExEceOHQMADBw4ENddd12rFEVERETUFloUfHJzc3H//fdj586dcHFxAQAUFRXhlltuwbp169C9e/fWrJGIiIioVbToqq45c+agtLQUR48eRWFhIQoLC5GSkoKSkhI8/vjjrV0jERERUato0R6fzZs3Y9u2bejfv780bsCAAVi5ciVPbiYiIqIOq0V7fMxmMzQaTb3xGo0GZrP5mosiIiIiagstCj633nornnjiCZw/f14al5WVhblz52Ls2LGtVhwRERFRa2pR8HnnnXdQUlICf39/9OnTB3369EFAQABKSkqwYsWK1q6RiIiIqFW06BwfX19f/Pbbb9i2bRuOHz8O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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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iRPtsUtERETUTNj1Z1/eeecd+Pv7Y9WqVdK0oKAg6W8hBJYsWYLXXnsNDz74IADgiy++gMFgwIYNG/Doo4/ixIkT2Lp1Kw4ePIiePXsCAD788EPce++9ePfdd+Hn51e/O0VERETNgl2PRP3000/o2bMn/vnPf8LHxwe33XYb/vOf/0jzU1JSkJ6ejvDwcGmam5sb+vTpg+joaABAdHQ03N3dpQAFAOHh4VAqlThw4ECl2y0uLobJZLJ5EBEREdWEXUPUqVOnsGLFCrRv3x7btm3D5MmT8dxzz2HNmjUAgPT0dACAwWCwWc5gMEjz0tPT4ePjYzNfrVbD09NTanO9BQsWwM3NTXr4+/vX9q4RERFRE2fXEGW1WnH77bfjrbfewm233YaJEydiwoQJWLlyZZ1uNzIyEnl5edLj7Nmzdbo9IiIianrsGqJ8fX0REhJiM61Tp05ITU0FABiNRgBARkaGTZuMjAxpntFoRGZmps18s9mMnJwcqc31dDod9Hq9zYOIiIioJuwaou644w4kJCTYTEtMTERgYCCAskHmRqMRUVFR0nyTyYQDBw4gLCwMABAWFobc3FzExMRIbXbu3Amr1Yo+ffrUw14QERFRc2TXq/OmT5+Ofv364a233sLDDz+MP//8E5988gk++eQTAIBCocC0adPw5ptvon379ggKCsLrr78OPz8/PPTQQwDKjlwNGzZMOg1YWlqKKVOm4NFHH+WVeURERFRn7BqievXqhfXr1yMyMhLz589HUFAQlixZgjFjxkhtZs2ahcLCQkycOBG5ubm48847sXXrVjg4OEhtvv76a0yZMgWDBw+GUqnEqFGjsHTpUnvsEhERETUTCiGEsHcR9mYymeDm5oa8vLxaHR8VHx+PNftOwxjYDulnkjGuX+sKY8CIiIhInrr6/q4uu//sCxEREVFjxBBFREREJANDFBEREZEMDFFEREREMjBEEREREcnAEEVEREQkA0MUERERkQwMUUREREQyMEQRERERycAQRURERCQDQxQRERGRDAxRRERERDIwRBERERHJwBBFREREJANDFBEREZEMDFFEREREMjBEEREREcnAEEVEREQkA0MUERERkQwMUUREREQyMEQRERERycAQRURERCQDQxQRERGRDAxRRERERDIwRBERERHJwBBFREREJANDFBEREZEMDFFEREREMjBEEREREcnAEEVEREQkA0MUERERkQwMUUREREQyMEQRERERycAQRURERCQDQxQRERGRDAxRRERERDIwRBERERHJwBBFREREJANDFBEREZEMDFFEREREMjBEEREREcnAEEVEREQkA0MUERERkQwMUUREREQyMEQRERERycAQRURERCSDXUPU3LlzoVAobB4dO3aU5hcVFSEiIgItWrSAi4sLRo0ahYyMDJt1pKamYsSIEXBycoKPjw9mzpwJs9lc37tCREREzYza3gV07twZO3bskJ6r1f8rafr06fjll1+wbt06uLm5YcqUKRg5ciT++OMPAIDFYsGIESNgNBqxb98+XLhwAU8++SQ0Gg3eeuutet8XIiIiaj7sHqLUajWMRmOF6Xl5efjss8/wzTff4O677wYArFq1Cp06dcL+/fvRt29f/Prrr4iPj8eOHTtgMBjQo0cPvPHGG3jppZcwd+5caLXa+t4dIiIiaibsPiYqKSkJfn5+aNOmDcaMGYPU1FQAQExMDEpLSxEeHi617dixIwICAhAdHQ0AiI6ORteuXWEwGKQ2Q4cOhclkwvHjx6vcZnFxMUwmk82DiIiIqCbsGqL69OmD1atXY+vWrVixYgVSUlLQv39/5OfnIz09HVqtFu7u7jbLGAwGpKenAwDS09NtAlT5/PJ5VVmwYAHc3Nykh7+/f+3uGBERETV5dj2dN3z4cOnvbt26oU+fPggMDMR3330HR0fHOttuZGQkZsyYIT03mUwMUkRERFQjdj+ddy13d3cEBwcjOTkZRqMRJSUlyM3NtWmTkZEhjaEyGo0VrtYrf17ZOKtyOp0Oer3e5kFERERUEw0qRBUUFODkyZPw9fVFaGgoNBoNoqKipPkJCQlITU1FWFgYACAsLAzHjh1DZmam1Gb79u3Q6/UICQmp9/qJiIio+bDr6bwXX3wR999/PwIDA3H+/HnMmTMHKpUKjz32GNzc3DB+/HjMmDEDnp6e0Ov1mDp1KsLCwtC3b18AwJAhQxASEoKxY8di4cKFSE9Px2uvvYaIiAjodDp77hoRERE1cXYNUefOncNjjz2GixcvwtvbG3feeSf2798Pb29vAMDixYuhVCoxatQoFBcXY+jQofjoo4+k5VUqFTZt2oTJkycjLCwMzs7OGDduHObPn2+vXSIiIqJmQiGEEPYuwt5MJhPc3NyQl5dXq+Oj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Пример оценки сбалансированности целевой переменной (цена автомобиля)\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Гистограмма распределения цены в обучающей выборке\n", + "sns.histplot(train_data['Price'], kde=True)\n", + "plt.title('Распределение цены в обучающей выборке')\n", + "plt.show()\n", + "\n", + "# Гистограмма распределения цены в контрольной выборке\n", + "sns.histplot(val_data['Price'], kde=True)\n", + "plt.title('Распределение цены в контрольной выборке')\n", + "plt.show()\n", + "\n", + "# Гистограмма распределения цены в тестовой выборке\n", + "sns.histplot(test_data['Price'], kde=True)\n", + "plt.title('Распределение цены в тестовой выборке')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Процесс конструирования признаков\n", + "Задача 1: Прогнозирование цен на автомобили\n", + "Цель технического проекта: Разработка модели машинного обучения для точного прогнозирования рыночной стоимости автомобилей.\n", + "\n", + "Задача 2: Оптимизация рекламных бюджетов\n", + "Цель технического проекта: Использование прогнозов цен на автомобили для оптимизации таргетинга рекламы и повышения конверсии на онлайн-площадках.\n", + "\n", + "\n", + "### Унитарное кодирование категориальных признаков (one-hot encoding)\n", + "\n", + "One-hot encoding: Преобразование категориальных признаков в бинарные векторы." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "# Пример категориальных признаков\n", + "categorical_features = ['Model', 'Category', 'Fuel type', 'Gear box type', 'Leather interior']\n", + "\n", + "# Применение one-hot encoding\n", + "train_data_encoded = pd.get_dummies(train_data, columns=categorical_features)\n", + "val_data_encoded = pd.get_dummies(val_data, columns=categorical_features)\n", + "test_data_encoded = pd.get_dummies(test_data, columns=categorical_features)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Дискретизация числовых признаков \n", + "это процесс преобразования непрерывных числовых значений в дискретные категории или интервалы (бины). Этот процесс может быть полезен по нескольким причинам" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Пример дискретизации признака 'year'\n", + "train_data_encoded['Year bin'] = pd.cut(train_data_encoded['Prod. year'], bins=5, labels=False)\n", + "val_data_encoded['Year bin'] = pd.cut(val_data_encoded['Prod. year'], bins=5, labels=False)\n", + "test_data_encoded['Year bin'] = pd.cut(test_data_encoded['Prod. year'], bins=5, labels=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ручной синтез\n", + "Создание новых признаков на основе экспертных знаний и логики предметной области. Например, для данных о продаже автомобилей можно создать признак \"возраст автомобиля\" как разницу между текущим годом и годом выпуска." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# Пример синтеза признака \"возраст автомобиля\"\n", + "train_data_encoded['Age'] = 2024 - train_data_encoded['Prod. year']\n", + "val_data_encoded['Age'] = 2024 - val_data_encoded['Prod. year']\n", + "test_data_encoded['Age'] = 2024 - test_data_encoded['Prod. year']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Масштабирование признаков - это процесс преобразования числовых признаков таким образом, чтобы они имели одинаковый масштаб. Это важно для многих алгоритмов машинного обучения, которые чувствительны к масштабу признаков, таких как линейная регрессия, метод опорных векторов (SVM) и нейронные сети." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler, MinMaxScaler\n", + "\n", + "# Пример масштабирования числовых признаков\n", + "numerical_features = ['Airbags', 'Age']\n", + "\n", + "scaler = StandardScaler()\n", + "train_data_encoded[numerical_features] = scaler.fit_transform(train_data_encoded[numerical_features])\n", + "val_data_encoded[numerical_features] = scaler.transform(val_data_encoded[numerical_features])\n", + "test_data_encoded[numerical_features] = scaler.transform(test_data_encoded[numerical_features])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Конструирование признаков с применением фреймворка Featuretools" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\entityset\\entityset.py:724: UserWarning: A Woodwork-initialized DataFrame was provided, so the following parameters were ignored: index\n", + " warnings.warn(\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\synthesis\\deep_feature_synthesis.py:169: UserWarning: Only one dataframe in entityset, changing max_depth to 1 since deeper features cannot be created\n", + " warnings.warn(\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\computational_backends\\feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n", + " df = pd.concat([df, default_df], sort=True)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\computational_backends\\feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n", + " df = pd.concat([df, default_df], sort=True)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n" + ] + } + ], + "source": [ + "import featuretools as ft\n", + "\n", + "# Определение сущностей\n", + "es = ft.EntitySet(id='car_data')\n", + "es = es.add_dataframe(dataframe_name='cars', dataframe=train_data_encoded, index='id')\n", + "\n", + "# Определение связей между сущностями (если есть)\n", + "# es = es.add_relationship(...)\n", + "\n", + "# Генерация признаков\n", + "feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='cars', max_depth=2)\n", + "\n", + "# Преобразование признаков для контрольной и тестовой выборок\n", + "val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_data_encoded.index)\n", + "test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_data_encoded.index)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Оценка качества каждого набора признаков\n", + "Предсказательная способность\n", + "Метрики: RMSE, MAE, R²\n", + "\n", + "Методы: Обучение модели на обучающей выборке и оценка на контрольной и тестовой выборках.\n", + "\n", + "Скорость вычисления\n", + "Методы: Измерение времени выполнения генерации признаков и обучения модели.\n", + "\n", + "Надежность\n", + "Методы: Кросс-валидация, анализ чувствительности модели к изменениям в данных.\n", + "\n", + "Корреляция\n", + "Методы: Анализ корреляционной матрицы признаков, удаление мультиколлинеарных признаков.\n", + "\n", + "Цельность\n", + "Методы: Проверка логической связи между признаками и целевой переменной, интерпретация результатов модели." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\entityset\\entityset.py:724: UserWarning: A Woodwork-initialized DataFrame was provided, so the following parameters were ignored: index\n", + " warnings.warn(\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\synthesis\\deep_feature_synthesis.py:169: UserWarning: Only one dataframe in entityset, changing max_depth to 1 since deeper features cannot be created\n", + " warnings.warn(\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\computational_backends\\feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n", + " df = pd.concat([df, default_df], sort=True)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\featuretools\\computational_backends\\feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n", + " df = pd.concat([df, default_df], sort=True)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n", + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\woodwork\\logical_types.py:841: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", + " series = series.replace(ww.config.get_option(\"nan_values\"), np.nan)\n" + ] + } + ], + "source": [ + "import featuretools as ft\n", + "\n", + "# Определение сущностей\n", + "es = ft.EntitySet(id='car_data')\n", + "es = es.add_dataframe(dataframe_name='cars', dataframe=train_data_encoded, index='id')\n", + "\n", + "# Генерация признаков\n", + "feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='cars', max_depth=2)\n", + "\n", + "# Преобразование признаков для контрольной и тестовой выборок\n", + "val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_data_encoded.index)\n", + "test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_data_encoded.index)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RMSE: 234661.34107821883\n", + "R²: 0.8029264507217629\n", + "MAE: 7964.677649030692\n", + "Cross-validated RMSE: 259310.71680259163\n", + "Train RMSE: 109324.02870848698\n", + "Train R²: 0.7887252013114727\n", + "Train MAE: 3471.173866063129\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\Egor\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\sklearn\\metrics\\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "image/png": "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ekm6cx7V/Pnzde7cOZ08edLcHhsbq969e+vMmTPKkiWLSpcurd9++83hOQAASO88PT21detWszSFhIRoypQpql+/vsXJACDzSvLiEN9++62aN28ub29vubu7KyoqSqNHj1afPn1SOmOycuYEMAAArLJt2zbVqVNHgwYNUs+ePTlfFwBSgDPdIMlnlI4aNUodO3ZUZGSkrl69qg8//FAjR4585LAAAGRm//zzjzp37nzXQkcVK1bUqVOn9O6771KaACANSPKMU9asWRUeHq5ChQpJunPYnI+Pj86cOaPAwMAUDZmcmHECAKQFCQkJmj17tgYOHKgrV66oTJky2r59u9zcnD79GADwkFJkxunmzZsOT+bh4SEvLy9FR0c/fFIAADKhLVu2qHLlyurcubOuXLkiSTp27Jj27dtncTIAwP049Wut2bNnK2vWrOb9+Ph4zZ8/X9mzZzfHUns5cgAA0ouLFy9qwIABmjt3rsN4q1atNGbMGC6vAQBpWJIP1cufP79sNtuDn8xm07Fjx5IlWErhUD0AQGqLj4/XzJkz9f777+vatWvmeKlSpTR9+nRVr17dunAAkIk50w2SPON04sSJR80FAECm1KVLF3366afmfT8/Pw0fPlxdunThnCYASCeSfI4TAAB4OGFhYXJxufNfbtu2bRUREaHu3btTmgAgHeEnNgAAySguLk5nzpxR/vz5zbHSpUtr/Pjxqly5sqpUqWJdOADAQ6M4AQCQTNauXauwsDAZhqHw8HB5eHiY23r27GldMADAI+NQPQAAHtHp06fVvHlzPfPMM9q/f78OHDigqVOnWh0LAJCMKE4AADyk2NhYjR49WkWLFtXXX39tjleqVEk1a9a0MBkAILkl6VC9qKioJD8hS3wDADKDX3/9Vd26dVNERIQ5lj17dn300Udq166duRgEACBjSFJxypYtW6LXcPpXQkLCIwUCACAtO336tHr06KGlS5eaYy4uLnr77bc1bNgwBQQEWJgOAJBSklSc1q5da/75xIkT6t+/v9q2bWuuDLR582Z99tlnGjVqVMqkBAAgjYiOjtby5cvN+1WrVtX06dMVGhpqXSgAQIqzGYZhOPOAOnXqqEOHDmrZsqXD+MKFCzVr1iz9/vvvyZkv2TlzdWAAAO6lX79++vzzzzVmzBi98cYbST4qAwCQtjjTDZwuTlmyZNHu3btV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169XYGBgSmVNFixHDiAzs9vtWrRokZo1ayZPT09zPCoqSlu2bFG9evUsTAcAQOpyphs4fahecHCwdu/ercWLF2v37t2Kjo5W+/bt9frrrzssFgEASFu2bdumrl27atu2bTp58qQGDBhgbvPz86M0AQDwAE7NOMXFxalo0aJasWKFihUrlpK5UgwzTgAym8uXL2vgwIGaPXu2/v2RnyVLFp08eVKPP/64xekAALBOil0A193dXbdv336kcACA1JGQkKAZM2YoJCREn376qVmaihcvrhUrVlCaAABwgtOLQ3Tt2lWjR4/mQogAkIZt3rxZlSpVUpcuXXT16lVJkq+vryZMmKDw8HDVrl3b4oQAAKQvTp/jtG3bNq1evVq//vqrSpUqJR8fH4ftS5cuTbZwAADndenSRTNmzHAYe+ONNzR69Gjlzp3bolQAAKRvThenbNmy6eWXX06JLACAZBAQEGD+uXTp0po+fbqefvppCxMBAJD+Ob0ceXrH4hAAMhrDMGSz2cz7N2/eVJUqVdSxY0d17txZbm5O/44MAIBMIUWWI7fb7Ro7dqyWL1+u2NhY1alTR4MHD2YJcgCwyLlz59S3b18VLFhQQ4cONcezZMmiXbt2ycXF6dNYAQDAfST5f9URI0Zo4MCBypo1q/LkyaPJkyera9euj/TF169fr8aNGysoKEg2m03fffddoo/5/fffVa5cOXl6eqpQoUKaP3/+I2UAgPQmLi5OEyZMUJEiRbRgwQKNHj1aR48eddiH0gQAQPJK8v+sn3/+uT7++GP98ssv+u677/TDDz9owYIFstvtD/3Fb9y4oTJlymj69OlJ2v/48eNq1KiRateurfDwcPXs2VMdOnTQL7/88tAZACA9Wbt2rUJDQ9W7d29dv35d0p0ZpoiICIuTAQCQsSX5HCdPT08dOXJEwcHB5piXl5eOHDmivHnzPnoQm03Lli1TkyZN7rvPu+++qx9//FH79u0zx1q0aKFr165p5cqVSfo6nOMEID06ffq0evfura+//tocs9ls6tixo0aMGKHs2bNbmA4AgPQpRS6AGx8fLy8vL4cxd3d3xcXFPVzKh7B582bVrVvXYax+/fravHnzfR8TExOjqKgohxsApBexsbEaPXq0ihYt6lCaKlWqpK1bt+qTTz6hNAEAkAqSvDiEYRhq27atPD09zbHbt2+rc+fODtdySsnrOJ0/f145c+Z0GMuZM6eioqJ069atey5UMWrUKIeTpgEgPfnkk0/Uv39/83727Nn10UcfqV27dpzHBABAKkry/7pt2rRRYGCg/P39zVurVq0UFBTkMJbWDBgwQJGRkebt1KlTVkcCgCTr2LGjChYsKBcXF3Xt2lURERFq3749pQkAgFSW5BmnefPmpWSOJMmVK5cuXLjgMHbhwgX5+fndd1l0T09Ph1kyAEirbt++rY0bN6pOnTrmmJeXlz777DP5+PgoNDTUunAAAGRy6epXllWqVNHq1asdxlatWqUqVapYlAgAksePP/6okiVLqkGDBjp48KDDtmrVqlGaAACwmKXFKTo6WuHh4QoPD5d0Z7nx8PBwnTx5UtKdw+xat25t7t+5c2cdO3ZM/fr108GDB/Xxxx/r66+/1jvvvGNFfAB4ZEePHlXjxo31/PPP6+jRo4qPj+dnGgAAaZClxWn79u0qW7asypYtK0nq1auXypYtqw8++ECSdO7cObNESVKBAgX0448/atWqVSpTpozGjx+v2bNnq379+pbkB4CHdfPmTX3wwQcqUaKEVqxYYY7XqFFDY8aMsTAZAAC4lyRfxymj4DpOAKxkGIa+//579ezZU3///bc5HhQUpHHjxqlFixay2WwWJgQAIPNwphskeXEIAMCjuXTpkt544w398ssv5pibm5veeecdvf/++/L19bUwHQAAeBCKEwCkkmzZsjkcflynTh1NnTpVxYoVszAVAABIinS1qh4ApGfu7u6aNm2agoOD9c0332jVqlWUJgAA0gmKEwCkgAMHDqh+/fravXu3w/gzzzyjw4cPq1mzZpzLBABAOsKhegCQjK5fv65hw4Zp0qRJio+P182bN7V+/XqHksRFuQEASH+YcQKAZGAYhhYuXKgiRYpo3Lhxio+PlySdPn1aZ86csTgdAAB4VBQnAHhEe/fuVa1atfT666/r3Llzku7MKg0ePFj79+9X3rx5LU4IAAAeFYfqAcBDunbtmoYMGaJp06YpISHBHH/hhRc0ceJEPfnkkxamAwAAyYniBAAPqUWLFg7XZCpYsKCmTJmihg0bWpgKAACkBA7VA4CH9P7770uSvL299eGHH2rfvn2UJgAAMihmnAAgCa5evap//vlHhQoVMseqVaum6dOnq1GjRsqXL5+F6QAAQEpjxgkAHsBut2v2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1K1bN61fv15nz561Oo5TGjRooHPnzunw4cPq3bu3hgwZorFjx95z39jYWElSjhw5lCVLltSMCQCZVlhYmDZv3qyvvvpKe/bs0SuvvKIGDRro8OHD99x/9uzZCgkJUfXq1Z36OhQnAECKio6O1uLFi/X222+rUaNGmj9//l37/PDDD6pYsaK8vLyUPXt287eKtWrV0t9//6133nnHnPmR7hyvHhoa6vAckyZNUv78+c3727Zt07PPPqvs2bPL399fNWvW1M6dO53O7+npqVy5cilfvnx6++23VbduXS1fvlzSnRmpJk2aaMSIEQoKClKRIkUk3T1Ldu3aNXXq1Ek5c+aUl5eXSpYsqRUrVpjbN2zYoOrVq8vb21vBwcHq3r27bty44XRWAMhsTp48qXnz5umbb75R9erVVbBgQfXp00dPP/205s2bd9f+t2/f1oIFC5yebZIoTgCAFPb111+raNGiKlKkiFq1aqW5c+fKMAxz+48//qimTZuqYcOG2rVrl1avXq1KlSpJkpYuXaq8efNq2LBh5nHpSXX9+nW1adNGGzZs0JYtW1S4cGE1bNhQ169ff6TX4+3tbc4sSdLq1at16NAhrVq1yqEM/ctut+u5557Txo0b9eWXX2r//v366KOP5OrqKkk6evSoGjRooJdffll79uzR4sWLtWHDBoWFhT1STgDIDPbu3auEhASFhIQoa9as5m3dunU6evToXfsvW7bM/P/BWW7JERgAgPuZM2eOWrVqJenOYW+RkZFat26datWqJUkaMWKEWrRooaFDh5qPKVOmjCQpICBArq6u8vX1Va5cuZz6us8884zD/VmzZilbtmxat26dnn/+eadfh2EYWr16tX755Rd169bNHPfx8dHs2bPl4eFxz8f99ttv2rp1qw4cOKCQkBBJ0pNPPmluHzVqlF5//XX17NlTklS4cGFNmTJFNWvW1IwZM+Tl5eV0VgDILKKjo+Xq6qodO3aYv5D6V9asWe/af/bs2Xr++eeVM2dOp78WxQkAkGIOHTqkrVu3atmyZZIkNzc3NW/eXHPmzDGLU3h4uDp27JjsX/vChQt677339Pvvv+vixYtKSEjQzZs3dfLkSaeeZ8WKFcqaNavi4uJkt9v12muvaciQIeb2UqVK3bc0SXdeX968ec3S9F+7d+/Wnj17tGDBAnPMMAzZ7XYdP35cxYoVcyovAGQmZcuWVUJCgi5evJjoOUvHjx/X2rVrzcOtnUVxAgCkmDlz5ig+Pl5BQUHmmGEY8vT01LRp0+Tv7y9vb2+nn9fFxcXhcD9JiouLc7jfpk0b/fPPP5o8ebLy5csnT09PValSxeEwu6SoXbu2ZsyYIQ8PDwUFBcnNzfG/zsSuA5LY64uOjlanTp3UvXv3u7Y98cQTTmUFgIwoOjpaR44cMe8fP35c4eHhCggIUEhIiF5//XW1bt1a48ePV9myZXXp0iWtXr1apUuXdljNde7cucqdO7eee+65h8pBcQIApIj4+Hh9/vnnGj9+vOrVq+ewrUmTJlq0aJE6d+6s0qVLa/Xq1WrXrt09n8fDw0MJCQkOYzly5ND58+dlGIa5YER4eLjDPhs3btTHH3+shg0bSpJOnTqly5cvO/06fHx8VKhQIacf96/SpUvr9OnTioiIuOesU7ly5bR///5H+hoAkJFt375dtWvXNu/36tVL0p1fkM2fP1/z5s3Thx9+qN69e+vMmTPKnj27nnrqKYfDsu12u+bPn6+2bdvedUhfUlGcAAApYsWKFbp69arat28vf39/h20vv/yy5syZo86dO2vw4MGqU6eOChYsqBYtWig+Pl4//fST3n33XUl3Vqhbv369WrRoIU9PT2XPnl21atXSpUuXNGbMGDVr1kwrV67Uzz//LD8/P/NrFC5cWF988YUqVKigqKgo9e3b96Fmtx5VzZo1VaNGDb388suaMGGCChUqpIMHD8pms6lBgwZ699139dRTTyksLEwdOnSQj4+P9u/fr1WrVmnatGmpnhcA0ppatWrddZTB/3J3d9fQoUMdzpX9LxcXF506deqRcrCqHgAgRcyZM0d169a9qzRJd4rT9u3btWfPHtWqVUvffPONli9frtDQUD3zzDPaunWrue+wYcN04sQJFSxYUDly5JAkFStWTB9//LGmT5+uMmXKaOvWrerTp89dX//q1asqV66c3njjDXXv3l2BgYEp+6LvY8mSJapYsaJatmyp4sWLq1+/fuYsWunSpbVu3TpFRESoevXqKlu2rD744AOHwxsBANazGQ+qbwAAAAAAZpwAAAAAIDEUJwAAAABIBMUJAAAAABJBcQIAAACARFCcAAAAACARFCcAAAAASATFCQAAAAASQXECAAAAgERQnAAAAAAgERQnAAAAAEgExQkAAAAAEvH/ADkyCDY9Et2KAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.ensemble import RandomForestRegressor\n", + "from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error\n", + "from sklearn.model_selection import cross_val_score\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Удаление строк с NaN\n", + "feature_matrix = feature_matrix.dropna()\n", + "val_feature_matrix = val_feature_matrix.dropna()\n", + "test_feature_matrix = test_feature_matrix.dropna()\n", + "\n", + "# Разделение данных на обучающую и тестовую выборки\n", + "X_train = feature_matrix.drop('Price', axis=1)\n", + "y_train = feature_matrix['Price']\n", + "X_val = val_feature_matrix.drop('Price', axis=1)\n", + "y_val = val_feature_matrix['Price']\n", + "X_test = test_feature_matrix.drop('Price', axis=1)\n", + "y_test = test_feature_matrix['Price']\n", + "\n", + "# Выбор модели\n", + "model = RandomForestRegressor(random_state=42)\n", + "\n", + "# Обучение модели\n", + "model.fit(X_train, y_train)\n", + "\n", + "# Предсказание и оценка\n", + "y_pred = model.predict(X_test)\n", + "\n", + "rmse = mean_squared_error(y_test, y_pred, squared=False)\n", + "r2 = r2_score(y_test, y_pred)\n", + "mae = mean_absolute_error(y_test, y_pred)\n", + "\n", + "print(f\"RMSE: {rmse}\")\n", + "print(f\"R²: {r2}\")\n", + "print(f\"MAE: {mae}\")\n", + "\n", + "# Кросс-валидация\n", + "scores = cross_val_score(model, X_train, y_train, cv=5, scoring='neg_mean_squared_error')\n", + "rmse_cv = (-scores.mean())**0.5\n", + "print(f\"Cross-validated RMSE: {rmse_cv}\")\n", + "\n", + "# Анализ важности признаков\n", + "feature_importances = model.feature_importances_\n", + "feature_names = X_train.columns\n", + "\n", + "# importance_df = pd.DataFrame({'Feature': feature_names, 'Importance': feature_importances})\n", + "# importance_df = importance_df.sort_values(by='Importance', ascending=False)\n", + "\n", + "# plt.figure(figsize=(10, 6))\n", + "# sns.barplot(x='Importance', y='Feature', data=importance_df)\n", + "# plt.title('Feature Importance')\n", + "# plt.show()\n", + "\n", + "# Проверка на переобучение\n", + "y_train_pred = model.predict(X_train)\n", + "\n", + "rmse_train = mean_squared_error(y_train, y_train_pred, squared=False)\n", + "r2_train = r2_score(y_train, y_train_pred)\n", + "mae_train = mean_absolute_error(y_train, y_train_pred)\n", + "\n", + "print(f\"Train RMSE: {rmse_train}\")\n", + "print(f\"Train R²: {r2_train}\")\n", + "print(f\"Train MAE: {mae_train}\")\n", + "\n", + "# Визуализация результатов\n", + "plt.figure(figsize=(10, 6))\n", + "plt.scatter(y_test, y_pred, alpha=0.5)\n", + "plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)\n", + "plt.xlabel('Actual Price')\n", + "plt.ylabel('Predicted Price')\n", + "plt.title('Actual vs Predicted Price')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Точность предсказаний: Модель показывает довольно высокий R² (0.8029), что указывает на хорошее объяснение вариации цен. Однако, значения RMSE и MAE довольно высоки, что говорит о том, что модель не очень точно предсказывает цены, особенно для высоких значений.\n", + "\n", + "Переобучение: Разница между RMSE на обучающей и тестовой выборках не очень большая, что указывает на то, что переобучение не является критическим. Однако, стоит быть осторожным и продолжать мониторинг этого показателя.\n", + "\n", + "Кросс-валидация: Значение RMSE после кросс-валидации немного выше, чем на тестовой выборке, что может указывать на некоторую нестабильность модели." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab_4/lab4.ipynb b/lab_4/lab4.ipynb new file mode 100644 index 0000000..14cb530 --- /dev/null +++ b/lab_4/lab4.ipynb @@ -0,0 +1,29 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Выбор бизнес-целей\n", + "### Задача регрессии:\n", + "\n", + "Цель: Предсказать цену автомобиля (Price) на основе других характеристик.\n", + "\n", + "Применение: Это может быть полезно для автосалонов, онлайн-площадок по продаже автомобилей, а также для частных лиц, которые хотят оценить рыночную стоимость своего автомобиля.\n", + "\n", + "Задача классификации:\n", + "\n", + "Цель: Классифицировать автомобили по категориям (например, \"Эконом\", \"Средний\", \"Премиум\") на основе цены и других характеристик.\n", + "\n", + "Применение: Это может быть полезно для маркетинговых кампаний, определения целевой аудитории, а также для анализа рынка автомобилей." + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}